n8n-nodes-keitaro
Version:
Полноценная интеграция с Keitaro Tracker API для n8n
1,249 lines (1,248 loc) • 155 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Keitaro = void 0;
// Функция для определения типа данных на основе ресурса
function getTypedResponse(resource, data) {
switch (resource) {
case 'campaign':
return data;
case 'stream':
return data;
case 'landing':
return data;
case 'offer':
return data;
case 'traffic':
return data;
case 'user':
return data;
case 'blacklist':
return data;
case 'bot':
return data;
case 'postback':
return data;
case 'source':
return data;
case 'conversion':
return data;
case 'domain':
return data;
case 'click':
return data;
case 'filter':
return data;
case 'settings':
return data;
case 'log':
return data;
case 'stats':
return data;
case 'group':
return data;
default:
return data;
}
}
// Функция для определения типа ответа
function getResponseData(response) {
// Проверяем разные форматы ответа API Keitaro
if (response && typeof response === 'object') {
// Проверяем поле status для определения успешности запроса
if ('status' in response) {
// Если статус не "success", выбрасываем ошибку
if (response.status !== 'success' && response.status !== 200) {
const errorMessage = response.error || response.message || 'Неизвестная ошибка API';
throw new Error(`Ошибка API: ${errorMessage}`);
}
}
if ('data' in response && response.data !== undefined) {
return response.data;
}
else if ('response' in response && response.response !== undefined) {
return response.response;
}
else if ('result' in response && response.result !== undefined) {
return response.result;
}
else if ('body' in response && response.body !== undefined) {
return response.body;
}
}
// Если не удалось найти данные в известных полях, возвращаем весь ответ
return response;
}
// Функция для безопасного логирования объектов
function safeStringify(obj) {
try {
return JSON.stringify(obj, (key, value) => {
// Исключаем циклические ссылки
if (key === 'socket' || key === '_httpMessage' || key === 'req' || key === 'res') {
return '[Circular]';
}
return value;
}, 2);
}
catch (error) {
return `[Не удалось сериализовать объект: ${error instanceof Error ? error.message : String(error)}]`;
}
}
// Хелпер для создания пути API
function getApiPath(resourceMap, resource, resourceId, operation) {
// Специальная обработка для клика с операцией getAll - используем /clicks/log
if (resource === 'click' && operation === 'getAll') {
return '/admin_api/v1/clicks/log';
}
// Специальная обработка для конверсий с операцией getAll - используем /conversions/log
if (resource === 'conversion' && operation === 'getAll') {
return '/admin_api/v1/conversions/log';
}
// Специальная обработка для статистики
if (resource === 'stats' && operation === 'getAll') {
return '/admin_api/v1/report/build';
}
// Специальная обработка для конверсий
if (resource === 'conversion') {
return `/admin_api/v1/conversions${resourceId ? `/${resourceId}` : ''}`;
}
const resourcePath = resourceMap[resource];
if (!resourcePath) {
throw new Error(`Неизвестный ресурс: ${resource}`);
}
return `/admin_api/v1/${resourcePath}${resourceId ? `/${resourceId}` : ''}`;
}
// Хелпер для форматирования URL API
function formatBaseUrl(domain) {
let baseUrl = domain.trim();
// Удаляем завершающий слеш, если он есть
if (baseUrl.endsWith('/')) {
baseUrl = baseUrl.slice(0, -1);
}
// Добавляем протокол, если его нет
if (!baseUrl.startsWith('http://') && !baseUrl.startsWith('https://')) {
baseUrl = `https://${baseUrl}`;
}
return baseUrl;
}
class Keitaro {
constructor() {
this.description = {
displayName: 'Keitaro',
name: 'keitaro',
icon: 'file:keitaro.svg',
group: ['transform'],
version: 1,
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
description: 'Работа с API Keitaro',
defaults: {
name: 'Keitaro',
},
inputs: ['main'],
outputs: ['main'],
credentials: [
{
name: 'keitaroApi',
required: true,
},
],
requestDefaults: {
baseURL: '={{$credentials.domain}}',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
},
properties: [
{
displayName: 'Ресурс',
name: 'resource',
type: 'options',
noDataExpression: true,
required: true,
options: [
{
name: 'Кампания',
value: 'campaign',
},
{
name: 'Поток',
value: 'stream',
},
{
name: 'Лендинг',
value: 'landing',
},
{
name: 'Оффер',
value: 'offer',
},
{
name: 'Трафик',
value: 'traffic',
},
{
name: 'Пользователь',
value: 'user',
},
{
name: 'Блэклист',
value: 'blacklist',
},
{
name: 'Бот',
value: 'bot',
},
{
name: 'Постбэк',
value: 'postback',
},
{
name: 'Источник',
value: 'source',
},
{
name: 'Конверсия',
value: 'conversion',
},
{
name: 'Домен',
value: 'domain',
},
{
name: 'Клик',
value: 'click',
},
{
name: 'Фильтр',
value: 'filter',
},
{
name: 'Настройки',
value: 'settings',
},
{
name: 'Лог',
value: 'log',
},
{
name: 'Статистика',
value: 'stats',
},
{
name: 'Группа',
value: 'group',
},
],
default: 'campaign',
description: 'Тип ресурса для работы',
},
{
displayName: 'Операция',
name: 'operation',
type: 'options',
noDataExpression: true,
required: true,
displayOptions: {
show: {
resource: [
'campaign',
'stream',
'landing',
'offer',
'traffic',
'user',
'blacklist',
'bot',
'postback',
'source',
'conversion',
'domain',
'click',
'filter',
'settings',
'log',
'stats',
'group',
],
},
},
options: [
{
name: 'Получить',
value: 'get',
description: 'Получить информацию',
action: 'Получить информацию',
},
{
name: 'Получить Все',
value: 'getAll',
description: 'Получить список всех записей',
action: 'Получить список всех записей',
},
{
name: 'Создать',
value: 'create',
description: 'Создать новую запись',
action: 'Создать новую запись',
},
{
name: 'Обновить',
value: 'update',
description: 'Обновить существующую запись',
action: 'Обновить существующую запись',
},
{
name: 'Удалить',
value: 'delete',
description: 'Удалить запись',
action: 'Удалить запись',
},
{
name: 'Тестировать',
value: 'test',
description: 'Тестировать постбэк (отправить тестовую конверсию)',
action: 'Тестировать постбэк',
displayOptions: {
show: {
resource: [
'postback',
],
},
},
},
],
default: 'get',
},
{
displayName: 'ID Кампании',
name: 'campaignId',
type: 'string',
required: true,
displayOptions: {
show: {
resource: ['campaign'],
operation: ['get', 'update', 'delete'],
},
},
default: '',
description: 'ID кампании',
},
{
displayName: 'Данные Кампании',
name: 'campaignData',
type: 'json',
required: true,
displayOptions: {
show: {
resource: ['campaign'],
operation: ['create', 'update'],
},
},
default: '{}',
description: 'Данные кампании в формате JSON',
},
{
displayName: 'ID Потока',
name: 'streamId',
type: 'string',
required: true,
displayOptions: {
show: {
resource: ['stream'],
operation: ['get', 'update', 'delete'],
},
},
default: '',
description: 'ID потока',
},
{
displayName: 'Данные Потока',
name: 'streamData',
type: 'json',
required: true,
displayOptions: {
show: {
resource: ['stream'],
operation: ['create', 'update'],
},
},
default: '{}',
description: 'Данные потока в формате JSON',
},
{
displayName: 'ID Лендинга',
name: 'landingId',
type: 'string',
required: true,
displayOptions: {
show: {
resource: ['landing'],
operation: ['get', 'update', 'delete'],
},
},
default: '',
description: 'ID лендинга',
},
{
displayName: 'Данные Лендинга',
name: 'landingData',
type: 'json',
required: true,
displayOptions: {
show: {
resource: ['landing'],
operation: ['create', 'update'],
},
},
default: '{}',
description: 'Данные лендинга в формате JSON',
},
{
displayName: 'ID Оффера',
name: 'offerId',
type: 'string',
required: true,
displayOptions: {
show: {
resource: ['offer'],
operation: ['get', 'update', 'delete'],
},
},
default: '',
description: 'ID оффера',
},
{
displayName: 'Данные Оффера',
name: 'offerData',
type: 'json',
required: true,
displayOptions: {
show: {
resource: ['offer'],
operation: ['create', 'update'],
},
},
default: '{}',
description: 'Данные оффера в формате JSON',
},
{
displayName: 'ID Пользователя',
name: 'userId',
type: 'string',
required: true,
displayOptions: {
show: {
resource: ['user'],
operation: ['get', 'update', 'delete'],
},
},
default: '',
description: 'ID пользователя',
},
{
displayName: 'Данные Пользователя',
name: 'userData',
type: 'json',
required: true,
displayOptions: {
show: {
resource: ['user'],
operation: ['create', 'update'],
},
},
default: '{}',
description: 'Данные пользователя в формате JSON',
},
{
displayName: 'ID Блэклиста',
name: 'blacklistId',
type: 'string',
required: true,
displayOptions: {
show: {
resource: ['blacklist'],
operation: ['get', 'update', 'delete'],
},
},
default: '',
description: 'ID записи в блэклисте',
},
{
displayName: 'Данные Блэклиста',
name: 'blacklistData',
type: 'json',
required: true,
displayOptions: {
show: {
resource: ['blacklist'],
operation: ['create', 'update'],
},
},
default: '{}',
description: 'Данные записи в блэклисте в формате JSON',
},
{
displayName: 'ID Бота',
name: 'botId',
type: 'string',
required: true,
displayOptions: {
show: {
resource: ['bot'],
operation: ['get', 'update', 'delete'],
},
},
default: '',
description: 'ID бота',
},
{
displayName: 'Данные Бота',
name: 'botData',
type: 'json',
required: true,
displayOptions: {
show: {
resource: ['bot'],
operation: ['create', 'update'],
},
},
default: '{}',
description: 'Данные бота в формате JSON',
},
{
displayName: 'ID Постбэка',
name: 'postbackId',
type: 'string',
required: true,
displayOptions: {
show: {
resource: ['postback'],
operation: ['get', 'update', 'delete'],
},
},
default: '',
description: 'ID постбэка',
},
{
displayName: 'Название постбэка',
name: 'postbackName',
type: 'string',
required: true,
displayOptions: {
show: {
resource: ['postback'],
operation: ['create', 'update'],
},
},
default: '',
description: 'Название постбэка',
},
{
displayName: 'URL постбэка',
name: 'postbackUrl',
type: 'string',
required: true,
displayOptions: {
show: {
resource: ['postback'],
operation: ['create', 'update'],
},
},
default: '',
placeholder: 'https://example.com/postback?clickid={sub_id_1}&status={status}',
description: 'URL для отправки постбэка. Можно использовать макросы: {sub_id_1}, {status}, {revenue}, и т.д.',
},
{
displayName: 'Статус',
name: 'postbackStatus',
type: 'options',
displayOptions: {
show: {
resource: ['postback'],
operation: ['create', 'update'],
},
},
options: [
{
name: 'Активный',
value: 'active',
},
{
name: 'Неактивный',
value: 'inactive',
},
],
default: 'active',
description: 'Статус постбэка',
},
{
displayName: 'Триггер',
name: 'postbackTrigger',
type: 'options',
displayOptions: {
show: {
resource: ['postback'],
operation: ['create', 'update'],
},
},
options: [
{
name: 'Конверсия',
value: 'conversion',
description: 'Срабатывает при любой конверсии',
},
{
name: 'Лид',
value: 'lead',
description: 'Срабатывает только при получении лида',
},
{
name: 'Продажа',
value: 'sale',
description: 'Срабатывает только при продаже',
},
],
default: 'conversion',
description: 'Когда отправлять постбэк',
},
{
displayName: 'HTTP метод',
name: 'postbackMethod',
type: 'options',
displayOptions: {
show: {
resource: ['postback'],
operation: ['create', 'update'],
},
},
options: [
{
name: 'GET',
value: 'GET',
},
{
name: 'POST',
value: 'POST',
},
],
default: 'GET',
description: 'HTTP метод для отправки постбэка',
},
{
displayName: 'Статусы лидов',
name: 'leadStatus',
type: 'string',
displayOptions: {
show: {
resource: ['postback'],
operation: ['create', 'update'],
},
},
default: 'lead,registration,new',
description: 'Статусы для лидов, перечисленные через запятую. Если партнерская сеть отправит один из этих статусов, Keitaro запишет его как "Лид".',
},
{
displayName: 'Статусы продаж',
name: 'saleStatus',
type: 'string',
displayOptions: {
show: {
resource: ['postback'],
operation: ['create', 'update'],
},
},
default: 'sale,paid,billed,completed',
description: 'Статусы для продаж, перечисленные через запятую. Если партнерская сеть отправит один из этих статусов, Keitaro запишет его как "Продажа".',
},
{
displayName: 'Отклоненные статусы',
name: 'rejectedStatus',
type: 'string',
displayOptions: {
show: {
resource: ['postback'],
operation: ['create', 'update'],
},
},
default: 'rejected,canceled,fake,fraud',
description: 'Статусы для отклоненных конверсий, перечисленные через запятую. Если партнерская сеть отправит один из этих статусов, Keitaro запишет его как "Отклонено".',
},
{
displayName: 'Дополнительные настройки',
name: 'postbackAdvanced',
type: 'boolean',
displayOptions: {
show: {
resource: ['postback'],
operation: ['create', 'update'],
},
},
default: false,
description: 'Показать дополнительные настройки постбэка',
},
{
displayName: 'Ключ постбэка',
name: 'postbackKey',
type: 'string',
displayOptions: {
show: {
resource: ['postback'],
operation: ['create', 'update'],
postbackAdvanced: [true],
},
},
default: '',
description: 'Ключ для аутентификации постбэка. Обеспечивает дополнительную безопасность.',
},
{
displayName: 'Валюта',
name: 'postbackCurrency',
type: 'string',
displayOptions: {
show: {
resource: ['postback'],
operation: ['create', 'update'],
postbackAdvanced: [true],
},
},
default: 'USD',
description: 'Валюта, используемая по умолчанию для конверсий.',
},
{
displayName: 'Таймаут (сек)',
name: 'postbackTimeout',
type: 'number',
displayOptions: {
show: {
resource: ['postback'],
operation: ['create', 'update'],
postbackAdvanced: [true],
},
},
default: 30,
description: 'Таймаут отправки постбэка в секундах.',
},
{
displayName: 'Количество повторных попыток',
name: 'postbackRetryCount',
type: 'number',
displayOptions: {
show: {
resource: ['postback'],
operation: ['create', 'update'],
postbackAdvanced: [true],
},
},
default: 3,
description: 'Сколько раз пытаться повторно отправить постбэк в случае ошибки.',
},
{
displayName: 'Задержка между попытками (сек)',
name: 'postbackRetryDelay',
type: 'number',
displayOptions: {
show: {
resource: ['postback'],
operation: ['create', 'update'],
postbackAdvanced: [true],
},
},
default: 60,
description: 'Задержка между повторными попытками в секундах.',
},
{
displayName: 'Кампании',
name: 'postbackCampaigns',
type: 'string',
displayOptions: {
show: {
resource: ['postback'],
operation: ['create', 'update'],
postbackAdvanced: [true],
},
},
default: '',
description: 'ID кампаний через запятую, для которых будет отправляться постбэк. Оставьте пустым для всех кампаний.',
},
{
displayName: 'Страны',
name: 'postbackCountries',
type: 'string',
displayOptions: {
show: {
resource: ['postback'],
operation: ['create', 'update'],
postbackAdvanced: [true],
},
},
default: '',
description: 'Коды стран через запятую, для которых будет отправляться постбэк. Оставьте пустым для всех стран.',
},
{
displayName: 'ID Источника',
name: 'sourceId',
type: 'string',
required: true,
displayOptions: {
show: {
resource: ['source'],
operation: ['get', 'update', 'delete'],
},
},
default: '',
description: 'ID источника',
},
{
displayName: 'Данные Источника',
name: 'sourceData',
type: 'json',
required: true,
displayOptions: {
show: {
resource: ['source'],
operation: ['create', 'update'],
},
},
default: '{}',
description: 'Данные источника в формате JSON',
},
{
displayName: 'ID Конверсии',
name: 'conversionId',
type: 'string',
required: true,
displayOptions: {
show: {
resource: ['conversion'],
operation: ['get', 'update', 'delete'],
},
},
default: '',
description: 'ID конверсии',
},
{
displayName: 'Данные Конверсии',
name: 'conversionData',
type: 'json',
required: true,
displayOptions: {
show: {
resource: ['conversion'],
operation: ['create', 'update'],
},
},
default: '{"click_id":"CLICK_ID_HERE","status":"lead","revenue":0}',
description: 'Данные конверсии в формате JSON. Обязательно укажите click_id.',
},
{
displayName: 'Упрощенное создание конверсии',
name: 'conversionSimple',
type: 'fixedCollection',
typeOptions: {
multipleValues: false,
},
displayOptions: {
show: {
resource: ['conversion'],
operation: ['create'],
},
},
default: {},
placeholder: 'Добавить параметры конверсии',
options: [
{
name: 'values',
displayName: 'Параметры конверсии',
values: [
{
displayName: 'ID клика (click_id)',
name: 'click_id',
type: 'string',
required: true,
default: '',
description: 'ID клика, для которого регистрируется конверсия',
},
{
displayName: 'Статус',
name: 'status',
type: 'options',
options: [
{
name: 'Лид',
value: 'lead',
},
{
name: 'Продажа',
value: 'sale',
},
{
name: 'Отклонено',
value: 'rejected',
},
{
name: 'Регистрация',
value: 'reg',
},
{
name: 'Депозит',
value: 'dep',
},
],
default: 'lead',
description: 'Статус конверсии',
},
{
displayName: 'Сумма дохода',
name: 'revenue',
type: 'number',
default: 0,
description: 'Сумма дохода от конверсии',
},
{
displayName: 'Валюта',
name: 'currency',
type: 'string',
default: 'USD',
description: 'Валюта дохода',
},
{
displayName: 'ID транзакции',
name: 'transaction_id',
type: 'string',
default: '',
description: 'ID транзакции (для повторных продаж/upsell)',
},
{
displayName: 'Комментарий',
name: 'comment',
type: 'string',
default: '',
description: 'Комментарий к конверсии',
},
],
},
],
description: 'Упрощенный способ создания конверсии',
},
{
displayName: 'ID Домена',
name: 'domainId',
type: 'string',
required: true,
displayOptions: {
show: {
resource: ['domain'],
operation: ['get', 'update', 'delete'],
},
},
default: '',
description: 'ID домена',
},
{
displayName: 'Данные Домена',
name: 'domainData',
type: 'json',
required: true,
displayOptions: {
show: {
resource: ['domain'],
operation: ['create', 'update'],
},
},
default: '{}',
description: 'Данные домена в формате JSON',
},
{
displayName: 'ID Клика',
name: 'clickId',
type: 'string',
required: true,
displayOptions: {
show: {
resource: ['click'],
operation: ['get', 'update', 'delete'],
},
},
default: '',
description: 'ID клика',
},
{
displayName: 'Данные Клика',
name: 'clickData',
type: 'json',
required: true,
displayOptions: {
show: {
resource: ['click'],
operation: ['create', 'update'],
},
},
default: '{}',
description: 'Данные клика в формате JSON',
},
{
displayName: 'ID Фильтра',
name: 'filterId',
type: 'string',
required: true,
displayOptions: {
show: {
resource: ['filter'],
operation: ['get', 'update', 'delete'],
},
},
default: '',
description: 'ID фильтра',
},
{
displayName: 'Данные Фильтра',
name: 'filterData',
type: 'json',
required: true,
displayOptions: {
show: {
resource: ['filter'],
operation: ['create', 'update'],
},
},
default: '{}',
description: 'Данные фильтра в формате JSON',
},
{
displayName: 'ID Настроек',
name: 'settingsId',
type: 'string',
required: true,
displayOptions: {
show: {
resource: ['settings'],
operation: ['get', 'update', 'delete'],
},
},
default: '',
description: 'ID настроек',
},
{
displayName: 'Данные Настроек',
name: 'settingsData',
type: 'json',
required: true,
displayOptions: {
show: {
resource: ['settings'],
operation: ['create', 'update'],
},
},
default: '{}',
description: 'Данные настроек в формате JSON',
},
{
displayName: 'ID Лога',
name: 'logId',
type: 'string',
required: true,
displayOptions: {
show: {
resource: ['log'],
operation: ['get', 'update', 'delete'],
},
},
default: '',
description: 'ID лога',
},
{
displayName: 'Данные Лога',
name: 'logData',
type: 'json',
required: true,
displayOptions: {
show: {
resource: ['log'],
operation: ['create', 'update'],
},
},
default: '{}',
description: 'Данные лога в формате JSON',
},
{
displayName: 'ID Статистики',
name: 'statsId',
type: 'string',
required: true,
displayOptions: {
show: {
resource: ['stats'],
operation: ['get', 'update', 'delete'],
},
},
default: '',
description: 'ID статистики',
},
{
displayName: 'Данные Статистики',
name: 'statsData',
type: 'json',
required: true,
displayOptions: {
show: {
resource: ['stats'],
operation: ['create', 'update'],
},
},
default: '{}',
description: 'Данные статистики в формате JSON',
},
{
displayName: 'ID Группы',
name: 'groupId',
type: 'string',
required: true,
displayOptions: {
show: {
resource: ['group'],
operation: ['get', 'update', 'delete'],
},
},
default: '',
description: 'ID группы',
},
{
displayName: 'Данные Группы',
name: 'groupData',
type: 'json',
required: true,
displayOptions: {
show: {
resource: ['group'],
operation: ['create', 'update'],
},
},
default: '{}',
description: 'Данные группы в формате JSON',
},
// Параметры для получения кликов
{
displayName: 'Параметры Фильтрации Кликов',
name: 'clickParameters',
type: 'fixedCollection',
placeholder: 'Добавить параметр',
default: {},
displayOptions: {
show: {
resource: [
'click',
],
operation: [
'getAll',
],
},
},
typeOptions: {
multipleValues: false,
},
description: 'Параметры для получения кликов',
options: [
{
name: 'range',
displayName: 'Диапазон дат',
values: [
{
displayName: 'Начальная дата',
name: 'from',
type: 'string',
default: '',
description: 'Начальная дата в формате YYYY-MM-DD',
},
{
displayName: 'Конечная Дата',
name: 'to',
type: 'string',
default: '',
description: 'Конечная дата в формате YYYY-MM-DD',
},
{
displayName: 'Часовой пояс',
name: 'timezone',
type: 'string',
default: '',
description: 'Часовой пояс, например Europe/Moscow',
},
]
},
{
name: 'interval',
displayName: 'Интервал',
values: [
{
displayName: 'Интервал',
name: 'interval',
type: 'options',
options: [
{
name: 'Сегодня',
value: 'today',
},
{
name: 'Вчера',
value: 'yesterday',
},
{
name: '7 дней назад',
value: '7_days_ago',
},
{
name: 'Первый день этой недели',
value: 'first_day_of_this_week',
},
{
name: '1 месяц назад',
value: '1_month_ago',
},
{
name: 'Первый день этого месяца',
value: 'first_day_of_this_month',
},
{
name: '1 год назад',
value: '1_year_ago',
},
{
name: 'Первый день этого года',
value: 'first_day_of_this_year',
},
{
name: 'За все время',
value: 'all_time',
},
],
default: '',
description: 'Предустановленный интервал времени',
},
],
},
{
name: 'mainFilters',
displayName: 'Основные фильтры',
values: [
{
displayName: 'ID Кампании',
name: 'campaign_id',
type: 'string',
default: '',
description: 'Фильтрация по ID кампании',
},
{
displayName: 'ID Потока',
name: 'stream_id',
type: 'string',
default: '',
description: 'Фильтрация по ID потока',
},
{
displayName: 'ID Лендинга',
name: 'landing_id',
type: 'string',
default: '',
description: 'Фильтрация по ID лендинга',
},
{