Instagram Leads Bulk Exporter

Export all filtered Instagram leads across all pages and datasets in one click

Size

13.4 KB

Version

1.0.1

Created

Oct 29, 2025

Updated

about 1 month ago

1// ==UserScript==
2// @name		Instagram Leads Bulk Exporter
3// @description		Export all filtered Instagram leads across all pages and datasets in one click
4// @version		1.0.1
5// @match		https://*.app.leads.cm/*
6// @icon		https://static.funnelcockpit.com/upload/6aYWQjYReAF8v4Jxe/6035cfd67a2e080d248f72796b49c8de.png
7// ==/UserScript==
8(function() {
9    'use strict';
10
11    console.log('Instagram Leads Bulk Exporter: Extension loaded');
12
13    // Utility function to wait for element
14    function waitForElement(selector, timeout = 10000) {
15        return new Promise((resolve, reject) => {
16            const element = document.querySelector(selector);
17            if (element) {
18                return resolve(element);
19            }
20
21            const observer = new MutationObserver(() => {
22                const element = document.querySelector(selector);
23                if (element) {
24                    observer.disconnect();
25                    resolve(element);
26                }
27            });
28
29            observer.observe(document.body, {
30                childList: true,
31                subtree: true
32            });
33
34            setTimeout(() => {
35                observer.disconnect();
36                reject(new Error(`Element ${selector} not found within ${timeout}ms`));
37            }, timeout);
38        });
39    }
40
41    // Utility function to wait/delay
42    function delay(ms) {
43        return new Promise(resolve => setTimeout(resolve, ms));
44    }
45
46    // Extract leads from current page
47    function extractLeadsFromCurrentPage() {
48        const leads = [];
49        const rows = document.querySelectorAll('#contacts-table tbody tr[data-username]');
50        
51        console.log(`Extracting ${rows.length} leads from current page`);
52        
53        rows.forEach(row => {
54            const username = row.getAttribute('data-username');
55            const cells = row.querySelectorAll('td');
56            
57            if (cells.length >= 9) {
58                const lead = {
59                    username: cells[0]?.textContent?.trim() || '',
60                    name: cells[1]?.textContent?.trim() || '',
61                    bio: cells[2]?.textContent?.trim() || '',
62                    category: cells[3]?.textContent?.trim() || '',
63                    followers: cells[4]?.textContent?.trim().replace(/\s+/g, '') || '',
64                    following: cells[5]?.textContent?.trim().replace(/\s+/g, '') || '',
65                    website: cells[6]?.querySelector('a')?.href || cells[6]?.textContent?.trim() || '',
66                    email: cells[7]?.textContent?.trim() || '',
67                    phone: cells[8]?.textContent?.trim() || ''
68                };
69                leads.push(lead);
70            }
71        });
72        
73        return leads;
74    }
75
76    // Get total number of pages
77    function getTotalPages() {
78        const totalPagesElement = document.querySelector('#totalPages');
79        return totalPagesElement ? parseInt(totalPagesElement.textContent) : 1;
80    }
81
82    // Get current page number
83    function getCurrentPage() {
84        const pageInput = document.querySelector('#pageInput');
85        return pageInput ? parseInt(pageInput.value) : 1;
86    }
87
88    // Navigate to specific page
89    async function navigateToPage(pageNumber) {
90        const pageInput = document.querySelector('#pageInput');
91        if (!pageInput) {
92            throw new Error('Page input not found');
93        }
94        
95        console.log(`Navigating to page ${pageNumber}`);
96        pageInput.value = pageNumber;
97        
98        // Trigger change event
99        const event = new Event('change', { bubbles: true });
100        pageInput.dispatchEvent(event);
101        
102        // Also try input event
103        const inputEvent = new Event('input', { bubbles: true });
104        pageInput.dispatchEvent(inputEvent);
105        
106        // Press Enter key
107        const enterEvent = new KeyboardEvent('keydown', {
108            key: 'Enter',
109            code: 'Enter',
110            keyCode: 13,
111            which: 13,
112            bubbles: true
113        });
114        pageInput.dispatchEvent(enterEvent);
115        
116        // Wait for page to load
117        await delay(2000);
118    }
119
120    // Get all dataset buttons
121    function getDatasetButtons() {
122        return Array.from(document.querySelectorAll('.dataset-button[data-dataset-index]'));
123    }
124
125    // Get currently selected dataset
126    function getCurrentDataset() {
127        const selectedBtn = document.querySelector('.dataset-button.selected');
128        return selectedBtn ? parseInt(selectedBtn.getAttribute('data-dataset-index')) : null;
129    }
130
131    // Navigate to specific dataset
132    async function navigateToDataset(datasetIndex) {
133        const datasetButtons = getDatasetButtons();
134        const targetButton = datasetButtons.find(btn => 
135            parseInt(btn.getAttribute('data-dataset-index')) === datasetIndex
136        );
137        
138        if (!targetButton) {
139            console.error(`Dataset button ${datasetIndex} not found`);
140            return false;
141        }
142        
143        console.log(`Navigating to dataset ${datasetIndex}`);
144        targetButton.click();
145        
146        // Wait for dataset to load
147        await delay(2000);
148        return true;
149    }
150
151    // Convert leads to CSV
152    function convertToCSV(leads) {
153        if (leads.length === 0) {
154            return '';
155        }
156        
157        const headers = ['Username', 'Name', 'Bio', 'Category', 'Followers', 'Following', 'Website', 'Email', 'Phone'];
158        const csvRows = [headers.join(',')];
159        
160        leads.forEach(lead => {
161            const row = [
162                escapeCSV(lead.username),
163                escapeCSV(lead.name),
164                escapeCSV(lead.bio),
165                escapeCSV(lead.category),
166                escapeCSV(lead.followers),
167                escapeCSV(lead.following),
168                escapeCSV(lead.website),
169                escapeCSV(lead.email),
170                escapeCSV(lead.phone)
171            ];
172            csvRows.push(row.join(','));
173        });
174        
175        return csvRows.join('\n');
176    }
177
178    // Escape CSV values
179    function escapeCSV(value) {
180        if (value === null || value === undefined) {
181            return '';
182        }
183        
184        const stringValue = String(value);
185        
186        // If value contains comma, quote, or newline, wrap in quotes and escape quotes
187        if (stringValue.includes(',') || stringValue.includes('"') || stringValue.includes('\n')) {
188            return '"' + stringValue.replace(/"/g, '""') + '"';
189        }
190        
191        return stringValue;
192    }
193
194    // Download CSV file
195    function downloadCSV(csvContent, filename) {
196        const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
197        const link = document.createElement('a');
198        const url = URL.createObjectURL(blob);
199        
200        link.setAttribute('href', url);
201        link.setAttribute('download', filename);
202        link.style.visibility = 'hidden';
203        
204        document.body.appendChild(link);
205        link.click();
206        document.body.removeChild(link);
207        
208        console.log(`CSV file "${filename}" downloaded successfully`);
209    }
210
211    // Main export function
212    async function exportAllLeads() {
213        const exportButton = document.querySelector('#export-all-leads-btn');
214        if (!exportButton) return;
215        
216        try {
217            // Disable button and show progress
218            exportButton.disabled = true;
219            exportButton.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Exporting...';
220            
221            const allLeads = [];
222            const datasetButtons = getDatasetButtons();
223            const totalDatasets = datasetButtons.length;
224            
225            console.log(`Starting export of ${totalDatasets} datasets`);
226            
227            // Store original position
228            const originalDataset = getCurrentDataset();
229            const originalPage = getCurrentPage();
230            
231            // Iterate through each dataset
232            for (let i = 0; i < totalDatasets; i++) {
233                const datasetIndex = parseInt(datasetButtons[i].getAttribute('data-dataset-index'));
234                
235                exportButton.innerHTML = `<i class="fas fa-spinner fa-spin"></i> Dataset ${i + 1}/${totalDatasets}...`;
236                
237                // Navigate to dataset
238                const success = await navigateToDataset(datasetIndex);
239                if (!success) {
240                    console.error(`Failed to navigate to dataset ${datasetIndex}`);
241                    continue;
242                }
243                
244                // Get total pages for this dataset
245                await delay(1000); // Wait for page count to update
246                const totalPages = getTotalPages();
247                console.log(`Dataset ${datasetIndex} has ${totalPages} pages`);
248                
249                // Iterate through each page in the dataset
250                for (let page = 1; page <= totalPages; page++) {
251                    exportButton.innerHTML = `<i class="fas fa-spinner fa-spin"></i> Dataset ${i + 1}/${totalDatasets}, Page ${page}/${totalPages}...`;
252                    
253                    // Navigate to page if not on page 1
254                    if (page > 1) {
255                        await navigateToPage(page);
256                    }
257                    
258                    // Extract leads from current page
259                    const pageLeads = extractLeadsFromCurrentPage();
260                    allLeads.push(...pageLeads);
261                    
262                    console.log(`Collected ${pageLeads.length} leads from dataset ${datasetIndex}, page ${page}. Total: ${allLeads.length}`);
263                }
264            }
265            
266            console.log(`Export complete! Total leads collected: ${allLeads.length}`);
267            
268            // Convert to CSV and download
269            if (allLeads.length > 0) {
270                const csvContent = convertToCSV(allLeads);
271                const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, -5);
272                const filename = `instagram_leads_all_${timestamp}.csv`;
273                downloadCSV(csvContent, filename);
274                
275                exportButton.innerHTML = `<i class="fas fa-check"></i> Exported ${allLeads.length} leads!`;
276            } else {
277                exportButton.innerHTML = '<i class="fas fa-exclamation-triangle"></i> No leads found';
278            }
279            
280            // Navigate back to original position
281            await delay(1000);
282            if (originalDataset) {
283                await navigateToDataset(originalDataset);
284                if (originalPage > 1) {
285                    await navigateToPage(originalPage);
286                }
287            }
288            
289            // Re-enable button after 3 seconds
290            setTimeout(() => {
291                exportButton.disabled = false;
292                exportButton.innerHTML = '<i class="fas fa-download"></i> Export All Leads';
293            }, 3000);
294            
295        } catch (error) {
296            console.error('Error during export:', error);
297            exportButton.innerHTML = '<i class="fas fa-exclamation-triangle"></i> Export Failed';
298            exportButton.disabled = false;
299            
300            setTimeout(() => {
301                exportButton.innerHTML = '<i class="fas fa-download"></i> Export All Leads';
302            }, 3000);
303        }
304    }
305
306    // Add the export all button
307    function addExportAllButton() {
308        // Find the filter button container
309        const filterContainer = document.querySelector('.filter-button-container');
310        if (!filterContainer) {
311            console.error('Filter button container not found');
312            return;
313        }
314        
315        // Check if button already exists
316        if (document.querySelector('#export-all-leads-btn')) {
317            console.log('Export All button already exists');
318            return;
319        }
320        
321        // Create the button
322        const exportAllButton = document.createElement('a');
323        exportAllButton.id = 'export-all-leads-btn';
324        exportAllButton.className = 'filter-button';
325        exportAllButton.href = 'javascript:void(0);';
326        exportAllButton.innerHTML = '<i class="fas fa-download"></i> Export All Leads';
327        exportAllButton.style.cssText = 'background-color: #28a745; color: white; margin-left: 10px;';
328        
329        // Add click event
330        exportAllButton.addEventListener('click', exportAllLeads);
331        
332        // Insert the button
333        filterContainer.appendChild(exportAllButton);
334        
335        console.log('Export All Leads button added successfully');
336    }
337
338    // Initialize the extension
339    async function init() {
340        console.log('Initializing Instagram Leads Bulk Exporter...');
341        
342        // Check if we're on the Instagram leads page
343        if (!window.location.href.includes('app.leads.cm/instagram')) {
344            console.log('Not on Instagram leads page, skipping initialization');
345            return;
346        }
347        
348        try {
349            // Wait for the filter container to be available
350            await waitForElement('.filter-button-container', 10000);
351            
352            // Add the export all button
353            addExportAllButton();
354            
355            console.log('Instagram Leads Bulk Exporter initialized successfully');
356        } catch (error) {
357            console.error('Failed to initialize extension:', error);
358        }
359    }
360
361    // Run initialization when DOM is ready
362    if (document.readyState === 'loading') {
363        document.addEventListener('DOMContentLoaded', init);
364    } else {
365        init();
366    }
367})();
Instagram Leads Bulk Exporter | Robomonkey