From 728e45c06597c67008a3cdfd2a4a8702519fe269 Mon Sep 17 00:00:00 2001 From: Hamza-Ayed Date: Fri, 5 Jun 2026 23:02:41 +0300 Subject: [PATCH] Auto-deploy: 2026-06-05 23:02:41 --- search_analyzer.js | 116 ++++++++++++++++++++++----------------------- 1 file changed, 57 insertions(+), 59 deletions(-) diff --git a/search_analyzer.js b/search_analyzer.js index ee92b33..0449566 100644 --- a/search_analyzer.js +++ b/search_analyzer.js @@ -44,81 +44,79 @@ }; try { - // 1. Name: The most reliable way is the profile link - const profileLinks = Array.from(cardEl.querySelectorAll('a[href*="/in/"]')) - .filter(a => a.innerText.trim().length > 2 && !a.innerText.includes('LinkedIn')); + // 1. Get all text lines, filter out empty ones + const rawLines = cardEl.innerText.split('\n').map(s => s.trim()).filter(s => s.length > 1); - if (profileLinks.length > 0) { - // Remove any injected language tags - let rawName = profileLinks[0].innerText.trim().split('\n')[0]; - data.name = rawName.replace(/English \(Australia\)|Auto|Translate/gi, '').trim(); - } else { - const nameEl = cardEl.querySelector('.entity-result__title-text, .search-result__title, span[dir="ltr"], h3'); - if (nameEl) data.name = nameEl.innerText.trim().split('\n')[0].replace(/English \(Australia\)|Auto/gi, '').trim(); + // 2. Filter out known noise (translation extensions, action buttons, connection degrees, etc) + const lines = rawLines.filter(s => { + const low = s.toLowerCase(); + // Remove degree connections + if (low.includes('degree connection')) return false; + if (low.includes('• 1st') || low.includes('• 2nd') || low.includes('• 3rd')) return false; + if (['1st', '2nd', '3rd', '3rd+'].includes(low)) return false; + // Remove translation artifacts + if (low.includes('english (australia)') || low === 'auto' || low === 'translate') return false; + // Remove action buttons (exact match to avoid removing headlines like "Connect with me") + if (low === 'connect' || low === 'message' || low === 'pending' || low === 'follow' || low === 'view profile') return false; + // Remove mutual connections line + if (low.includes('mutual connection')) return false; + // Remove injected button text from this extension + if (low.includes('scan investor')) return false; + if (low.includes('تجاهله') || low.includes('تواصل معه') || low.includes('error')) return false; + return true; + }); + + // 3. Assign the cleaned lines + // Remove duplicates if the name appears twice (e.g., "Hamza" then "Hamza • 2nd" filtered to "Hamza") + let uniqueLines = []; + lines.forEach(l => { if (!uniqueLines.includes(l)) uniqueLines.push(l); }); + + if (uniqueLines.length > 0) data.name = uniqueLines[0]; + if (uniqueLines.length > 1) data.headline = uniqueLines[1]; + if (uniqueLines.length > 2) data.location = uniqueLines[2]; + + // Everything else is summary + if (uniqueLines.length > 3) { + data.summary = uniqueLines.slice(3, 8).join(' | '); } - // 2. Headline - const headlineEl = cardEl.querySelector('.entity-result__primary-subtitle, [class*="subtitle"], .linked-area'); - if (headlineEl) data.headline = headlineEl.innerText.trim(); - - // 3. Location - const locationEl = cardEl.querySelector('.entity-result__secondary-subtitle, .search-result__info'); - if (locationEl) data.location = locationEl.innerText.trim(); - - // 4. Summary - const summaryEl = cardEl.querySelector('.entity-result__summary, .search-result__snippets'); - if (summaryEl) data.summary = summaryEl.innerText.trim(); - - // Clean up - if (data.name) data.name = data.name.replace(/View .* profile/gi, '').trim(); - - if (!data.name) { - data.name = 'مستثمر محتمل'; - } } catch (e) { console.error('[LJA] Extraction failed', e); - data.name = 'مستثمر محتمل'; } + if (!data.name || data.name.length < 2) data.name = 'مستثمر محتمل'; + if (!data.headline) data.headline = 'لا يوجد مسمى وظيفي'; + return data; } // ─── Find Cards Logic ────────────────────────────────────────────────────── function findCards() { - let cards = []; let uniqueCards = new Set(); - // Method 1: The official LinkedIn search result container class - let containerElements = document.querySelectorAll('.reusable-search__result-container, .search-entity, .entity-result__item'); - if (containerElements.length > 0) { - containerElements.forEach(el => uniqueCards.add(el)); - console.log('[LJA] Found cards via container classes:', uniqueCards.size); - } - - // Method 2: Fallback to finding profile links and going up to the list item - if (uniqueCards.size === 0) { - let profileLinks = Array.from(document.querySelectorAll('a[href*="/in/"]')) - .filter(a => a.innerText.trim().length > 2 && !a.querySelector('img')); - - profileLinks.forEach(link => { - // Find the nearest list item or large div container - let container = link.closest('li') || link.closest('div.mb1') || link.parentElement.parentElement.parentElement; - if (container && container.innerText.length > 20) { - uniqueCards.add(container); - } - }); - console.log('[LJA] Found cards via profile links:', uniqueCards.size); - } - - // Convert Set to Array and filter out translation extension dropdowns - cards = Array.from(uniqueCards).filter(card => { - const txt = card.innerText.toLowerCase(); - // Must not be a language selector dropdown - if (txt.includes('english (australia)') && txt.length < 50) return false; - // Should have some decent amount of text - return txt.length > 20; + // The most bulletproof way to find a profile card is to find the main Action Button (Connect/Message/Follow) + // and then go up the DOM tree until we find the container that holds the whole profile + let buttons = Array.from(document.querySelectorAll('button')).filter(btn => { + let txt = btn.innerText.toLowerCase().trim(); + return txt === 'connect' || txt === 'message' || txt === 'pending' || txt === 'follow'; }); + buttons.forEach(btn => { + let container = btn.parentElement; + // Go up the DOM tree (max 10 levels) to find the list item + for(let i=0; i<10; i++) { + if (!container) break; + // A valid card container is usually an LI and contains an image + if (container.tagName === 'LI' && container.querySelector('img')) { + uniqueCards.add(container); + break; + } + container = container.parentElement; + } + }); + + const cards = Array.from(uniqueCards); + console.log('[LJA] Found valid profile cards:', cards.length); return cards; }