UNPKG

n8n-nodes-instantly-dev

Version:

n8n community node for Instantly API v2

353 lines 14.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.InstantlyApi = void 0; const n8n_workflow_1 = require("n8n-workflow"); const generic_functions_1 = require("../generic.functions"); const OperationRouter_1 = require("./operations/OperationRouter"); const LeadParameters_1 = require("./parameters/LeadParameters"); const CampaignParameters_1 = require("./parameters/CampaignParameters"); const AccountParameters_1 = require("./parameters/AccountParameters"); const AnalyticsParameters_1 = require("./parameters/AnalyticsParameters"); // Helper function to format dates for Instantly API (YYYY-MM-DD format) function formatDateForApi(dateInput) { if (!dateInput || dateInput === '') { return ''; } let dateString = String(dateInput); // Handle n8n DateTime objects that come as "[DateTime: 2025-06-26T13:52:08.271Z]" if (dateString.startsWith('[DateTime: ') && dateString.endsWith(']')) { dateString = dateString.slice(11, -1); // Remove "[DateTime: " and "]" } // Handle ISO datetime strings (e.g., "2025-06-19T13:52:45.316Z") if (dateString.includes('T')) { dateString = dateString.split('T')[0]; } // Handle date strings that might have time components separated by space if (dateString.includes(' ')) { dateString = dateString.split(' ')[0]; } // Validate the resulting date format (should be YYYY-MM-DD) const dateRegex = /^\d{4}-\d{2}-\d{2}$/; if (!dateRegex.test(dateString)) { // Try to parse as Date and format try { const parsedDate = new Date(dateInput); if (!isNaN(parsedDate.getTime())) { // Format as YYYY-MM-DD const year = parsedDate.getFullYear(); const month = String(parsedDate.getMonth() + 1).padStart(2, '0'); const day = String(parsedDate.getDate()).padStart(2, '0'); return `${year}-${month}-${day}`; } } catch (error) { console.warn('Failed to parse date:', dateInput, error); } // If all parsing fails, return empty string to avoid API errors console.warn('Invalid date format for Instantly API:', dateInput); return ''; } return dateString; } // Helper function for pagination async function paginateInstantlyApi(context, endpoint, resourceName) { let allItems = []; let startingAfter; let hasMore = true; let pageCount = 0; while (hasMore) { pageCount++; const queryParams = { limit: 100 }; // Use max limit for efficiency if (startingAfter) { queryParams.starting_after = startingAfter; } const response = await generic_functions_1.instantlyApiRequest.call(context, 'GET', endpoint, {}, queryParams); // Handle response structure - items are in 'items' array for paginated responses let itemsData = []; if (response.items && Array.isArray(response.items)) { itemsData = response.items; } else if (response.data && Array.isArray(response.data)) { itemsData = response.data; } else if (Array.isArray(response)) { itemsData = response; } else { console.warn('Unexpected response structure:', response); } if (itemsData.length > 0) { allItems = allItems.concat(itemsData); } else { hasMore = false; } // Check if there are more pages using next_starting_after if (response.next_starting_after && itemsData.length > 0) { startingAfter = response.next_starting_after; } else { hasMore = false; } // Safety check to prevent infinite loops if (pageCount > 1000) { console.warn('Pagination stopped after 1000 pages to prevent infinite loop'); break; } } return allItems; } // Helper function to get campaigns for dropdown async function getCampaigns(filter) { try { // Get campaigns using our existing pagination function const responseData = await generic_functions_1.instantlyApiRequest.call(this, 'GET', '/api/v2/campaigns', {}, { limit: 100 }); // Handle response structure - campaigns are in 'items' array for paginated responses let campaigns = []; if (responseData.items && Array.isArray(responseData.items)) { campaigns = responseData.items; } else if (Array.isArray(responseData)) { campaigns = responseData; } // Filter campaigns if filter is provided const filteredCampaigns = campaigns.filter((campaign) => { if (!filter) return true; const campaignName = `${campaign.name || ''}`.toLowerCase(); return campaignName.includes(filter.toLowerCase()); }); // Map campaigns to dropdown options const campaignOptions = filteredCampaigns.map((campaign) => ({ name: campaign.name || `Campaign ${campaign.id}`, value: campaign.id, })); return { results: campaignOptions, }; } catch (error) { console.error('Error fetching campaigns for dropdown:', error); return { results: [], }; } } // Helper function to get email accounts for dropdown async function getEmailAccounts(filter) { try { // Get email accounts using the accounts API endpoint const responseData = await generic_functions_1.instantlyApiRequest.call(this, 'GET', '/api/v2/accounts', {}, { limit: 100 }); // Handle response structure - accounts are in 'items' array for paginated responses let accounts = []; if (responseData.items && Array.isArray(responseData.items)) { accounts = responseData.items; } else if (Array.isArray(responseData)) { accounts = responseData; } // Filter accounts if filter is provided const filteredAccounts = accounts.filter((account) => { if (!filter) return true; const accountEmail = `${account.email || ''}`.toLowerCase(); const accountName = `${account.first_name || ''} ${account.last_name || ''}`.toLowerCase().trim(); return accountEmail.includes(filter.toLowerCase()) || accountName.includes(filter.toLowerCase()); }); // Map accounts to dropdown options const accountOptions = filteredAccounts.map((account) => { const displayName = account.first_name && account.last_name ? `${account.email} (${account.first_name} ${account.last_name})` : account.email; return { name: displayName || `Account ${account.id}`, value: account.email, }; }); return { results: accountOptions, }; } catch (error) { console.error('Error fetching email accounts for dropdown:', error); return { results: [], }; } } // Helper function to get leads for dropdown async function getLeads(filter) { try { // Get leads using the leads list API endpoint const body = { limit: 100, }; // Add search filter if provided if (filter) { body.search = filter; } const response = await generic_functions_1.instantlyApiRequest.call(this, 'POST', '/api/v2/leads/list', body); const leadOptions = []; if (response.items && Array.isArray(response.items)) { for (const lead of response.items) { const name = `${lead.first_name || ''} ${lead.last_name || ''}`.trim() || lead.email || 'Unknown Lead'; const value = lead.id; leadOptions.push({ name: `${name} (${lead.email || 'No email'})`, value, }); } } return { results: leadOptions, }; } catch (error) { console.error('Error fetching leads for dropdown:', error); return { results: [], }; } } class InstantlyApi { constructor() { this.description = { displayName: 'Instantly', name: 'instantly', icon: 'file:instantly.svg', group: ['transform'], version: 1, subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}', description: 'Interact with Instantly API', defaults: { name: 'Instantly', }, inputs: ["main" /* NodeConnectionType.Main */], outputs: ["main" /* NodeConnectionType.Main */], credentials: [ { name: 'instantlyApi', required: true, }, ], properties: [ { displayName: 'Resource', name: 'resource', type: 'options', noDataExpression: true, options: [ { name: 'Account', value: 'account', }, { name: 'Analytics', value: 'analytics', }, { name: 'Campaign', value: 'campaign', }, { name: 'Lead', value: 'lead', }, ], default: 'campaign', }, // CAMPAIGN OPERATIONS - Using modular parameters ...CampaignParameters_1.campaignParameters, // LEAD OPERATIONS - Using modular parameters ...LeadParameters_1.leadParameters, // ACCOUNT OPERATIONS - Using modular parameters ...AccountParameters_1.accountParameters, // ANALYTICS OPERATIONS - Using modular parameters ...AnalyticsParameters_1.analyticsParameters, ], }; this.methods = { listSearch: { getCampaigns, getEmailAccounts, getLeads, }, loadOptions: { // Load email accounts for multi-select dropdown async getEmailAccounts() { try { const response = await generic_functions_1.instantlyApiRequest.call(this, 'GET', '/api/v2/accounts'); const accounts = response.items || []; return accounts.map((account) => ({ name: `${account.email} (${account.first_name} ${account.last_name})`, value: account.email, })); } catch (error) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Failed to load email accounts: ${error.message}`); } }, }, }; } async execute() { const items = this.getInputData(); const returnData = []; // Helper function to extract campaign ID from resourceLocator const getCampaignId = (i) => { const campaignLocator = this.getNodeParameter('campaignId', i); if (typeof campaignLocator === 'string') { // Backward compatibility - if it's still a string return campaignLocator; } // Extract value from resourceLocator return campaignLocator.value || campaignLocator; }; // Helper function to extract email from resourceLocator const getEmailAccount = (i) => { const emailLocator = this.getNodeParameter('emailAccount', i); if (typeof emailLocator === 'string') { // Backward compatibility - if it's still a string return emailLocator; } // Extract value from resourceLocator return emailLocator.value || emailLocator; }; for (let i = 0; i < items.length; i++) { try { const resource = this.getNodeParameter('resource', i); const operation = this.getNodeParameter('operation', i); let responseData; if (resource === 'campaign') { // Use the modular OperationRouter for Campaign operations responseData = await OperationRouter_1.OperationRouter.execute(this, i, resource, operation); } else if (resource === 'lead') { // Use the modular OperationRouter for Lead operations responseData = await OperationRouter_1.OperationRouter.execute(this, i, resource, operation); } else if (resource === 'account') { // Use the modular OperationRouter for Account operations responseData = await OperationRouter_1.OperationRouter.execute(this, i, resource, operation); } else if (resource === 'analytics') { // Use the modular OperationRouter for Analytics operations responseData = await OperationRouter_1.OperationRouter.execute(this, i, resource, operation); } const executionData = this.helpers.constructExecutionMetaData(this.helpers.returnJsonArray(responseData), { itemData: { item: i } }); returnData.push(...executionData); } catch (error) { if (this.continueOnFail()) { const executionData = this.helpers.constructExecutionMetaData([{ json: { error: error.message } }], { itemData: { item: i } }); returnData.push(...executionData); continue; } throw error; } } return [returnData]; } } exports.InstantlyApi = InstantlyApi; //# sourceMappingURL=InstantlyApi.node.js.map