import 'dotenv/config'; import neo4j from 'neo4j-driver'; import { fileURLToPath } from 'url'; import * as path from 'path'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); // --- KONFIGURATION (TDC ENHANCED) --- const ORG_CAPABILITIES = [ // Core Tech 'Cyber Security', 'Threat Intelligence', 'Graph Database', 'Neo4j', 'AI', 'Machine Learning', 'Network Analysis', 'Monitoring', 'SaaS', // TDC Infrastruktur & Netværk '5G', 'Private 5G', 'Fiber', 'Coax', 'IoT', 'NB-IoT', 'LTE-M', 'MPLS', 'SD-WAN', 'Network-as-a-Service', 'Kritisk Infrastruktur', // TDC Sikkerhed (Heavy Duty) 'SOC', 'Security Operations Center', 'DDoS', 'DDoS Protection', 'Firewall', 'Smart Security', 'Log Management', 'SIEM', 'NIS2', 'Compliance', 'Vulnerability Management', 'Endpoint Protection', // Cloud & Unified Comms 'Unified Communications', 'Teams', 'Cisco Webex', 'Cloud Key', 'Managed Cloud', 'Azure', 'Device Management', 'MDM' ]; const UPSCALE_KEYWORDS = [ 'Innovation', 'R&D', 'Udvikling', 'Prototype', 'Partnerskab', 'PoC', 'Skalering', 'Transformation', 'Digitalisering' ]; // Kilder der ofte har "under-threshold" udbud (ikke i TED) const DEEP_SOURCES = [ { name: 'Udbud.dk (DK)', url: 'https://udbud.dk/rss.aspx', type: 'RSS', country: 'DK' }, { name: 'Doffin (NO)', url: 'https://doffin.no/RSS', type: 'RSS', country: 'NO' }, { name: 'Mercell (Nordic)', url: 'https://mercell.com/rss', type: 'RSS', country: 'Nordic' } ]; // --- LOGIK --- interface Tender { id: string; title: string; description: string; buyer: string; value?: number; currency?: string; deadline?: string; source: string; url: string; } interface StrategicFit { score: number; // 0-100 matches: string[]; isUpscaleOpportunity: boolean; upscaleReason?: string; } async function runHarvester() { console.log('🕵️‍♂️ Operation Smart Hunter v2.0 (TDC Edition): Initializing...'); // 1. Forbind til Neo4j Cloud if (!process.env.NEO4J_URI) { console.error("❌ Mangler NEO4J_URI i .env"); process.exit(1); } const driver = neo4j.driver( process.env.NEO4J_URI, neo4j.auth.basic(process.env.NEO4J_USER!, process.env.NEO4J_PASSWORD!) ); const session = driver.session(); try { // 2. Fetch Data (Simuleret RSS parsing for demo) console.log('📡 Scanning Deep Sources (Non-TED)...'); const tenders = await fetchTenders(); console.log(`📥 Downloaded ${tenders.length} potential tenders.`); let validLeads = 0; for (const tender of tenders) { // 3. Analyser Strategisk Fit const fit = calculateStrategicFit(tender); // Filter: Vi gemmer kun hvis score > 40 if (fit.score > 40) { console.log(`🎯 MATCH FOUND: ${tender.title} (Score: ${fit.score}%)`); console.log(` - Keywords: ${fit.matches.join(', ')}`); // 4. Ingest i Grafen await session.run(` MERGE (t:Tender {id: $id}) SET t += $props, t.ingestedAt = datetime() MERGE (b:Buyer {name: $buyer}) MERGE (b)-[:ISSUED]->(t) MERGE (org:Organization {name: 'WidgeTDC'}) MERGE (org)-[f:POTENTIAL_FIT]->(t) SET f.score = $score, f.matches = $matches, f.upscale = $upscale, f.rationale = $rationale `, { id: tender.id, buyer: tender.buyer, props: { title: tender.title, description: tender.description, source: tender.source, url: tender.url, deadline: tender.deadline }, score: fit.score, matches: fit.matches, upscale: fit.isUpscaleOpportunity, rationale: fit.upscaleReason || '' }); validLeads++; } } console.log(`✅ Hunt Complete. Ingested ${validLeads} strategic opportunities.`); } catch (err) { console.error('❌ Harvester Failed:', err); } finally { await session.close(); await driver.close(); } } // --- HJÆLPEFUNKTIONER --- async function fetchTenders(): Promise { // MOCK DATA - Demonstrerer den nye bredde i TDC's kapabiliteter return [ { id: 'DK-2025-001', title: 'Etablering af SOC og beredskab til Region Hovedstaden', description: 'Vi søger en leverandør til 24/7 overvågning af netværkstrafik, Log Management og Threat Intelligence.', buyer: 'Region Hovedstaden', source: 'Udbud.dk (DK)', url: 'https://udbud.dk/...' }, { id: 'DK-2025-002', title: 'Landsdækkende IoT-netværk til forsyningssektoren', description: 'Udrulning af NB-IoT sensorer til vandmåling. Kræver stabil 5G/NB-IoT dækning.', buyer: 'HOFOR', source: 'Udbud.dk (DK)', url: 'https://udbud.dk/...' }, { id: 'NO-2025-992', title: 'Sikker kommunikationsplatform (Unified Comms)', description: 'Modernisering af telefoni og video. Skal integrere med Microsoft Teams og Cisco udstyr.', buyer: 'Oslo Kommune', source: 'Doffin (NO)', url: 'https://doffin.no/...' }, { id: 'SE-2025-XYZ', title: 'Rengøring af kommunale bygninger', description: 'Daglig rengøring...', buyer: 'Malmö Stad', source: 'Opic (SE)', url: 'https://opic.se/...' } ]; } function calculateStrategicFit(tender: Tender): StrategicFit { const text = (tender.title + ' ' + tender.description).toLowerCase(); // 1. Tæl Capability Matches const matches = ORG_CAPABILITIES.filter(cap => text.includes(cap.toLowerCase())); // 2. Beregn Base Score // Vi er nu mere aggressive: Jo flere matches, jo højere score. let score = (matches.length / 2) * 100; // 2 matches = 100% (fordi vi søger niche-fit) if (score > 100) score = 100; // 3. Tjek for Upscaling const upscaleMatches = UPSCALE_KEYWORDS.filter(kw => text.includes(kw.toLowerCase())); const isUpscale = upscaleMatches.length > 0; if (isUpscale) score += 10; return { score: Math.round(score), matches, isUpscaleOpportunity: isUpscale, upscaleReason: isUpscale ? `Innovation keywords: ${upscaleMatches.join(', ')}` : undefined }; } // Start runHarvester();