147 lines
4.3 KiB
JavaScript

#!/usr/bin/env node
/**
* LinkedIn Parser - Refactored
*
* Uses core-parser for browser management and linkedin-strategy for parsing logic
*/
const path = require("path");
const fs = require("fs");
const CoreParser = require("../core-parser");
const { linkedinStrategy } = require("./strategies/linkedin-strategy");
const { logger, analyzeBatch, checkOllamaStatus } = require("ai-analyzer");
// Load environment variables
require("dotenv").config({ path: path.join(__dirname, ".env") });
// Configuration from environment
const LINKEDIN_USERNAME = process.env.LINKEDIN_USERNAME;
const LINKEDIN_PASSWORD = process.env.LINKEDIN_PASSWORD;
const HEADLESS = process.env.HEADLESS !== "false";
const SEARCH_KEYWORDS =
process.env.SEARCH_KEYWORDS || "layoff,downsizing,job cuts";
const LOCATION_FILTER = process.env.LOCATION_FILTER;
const ENABLE_AI_ANALYSIS = process.env.ENABLE_AI_ANALYSIS === "true";
const MAX_RESULTS = parseInt(process.env.MAX_RESULTS) || 50;
/**
* Main LinkedIn parser function
*/
async function startLinkedInParser(options = {}) {
const coreParser = new CoreParser({
headless: HEADLESS,
timeout: 30000,
});
try {
logger.step("🚀 LinkedIn Parser Starting...");
// Validate credentials
if (!LINKEDIN_USERNAME || !LINKEDIN_PASSWORD) {
throw new Error(
"LinkedIn credentials not found. Please set LINKEDIN_USERNAME and LINKEDIN_PASSWORD in .env file"
);
}
// Parse keywords
const keywords = SEARCH_KEYWORDS.split(",").map((k) => k.trim());
logger.info(`🔍 Search Keywords: ${keywords.join(", ")}`);
logger.info(`📍 Location Filter: ${LOCATION_FILTER || "None"}`);
logger.info(
`🧠 AI Analysis: ${ENABLE_AI_ANALYSIS ? "Enabled" : "Disabled"}`
);
logger.info(`📊 Max Results: ${MAX_RESULTS}`);
// Run LinkedIn parsing strategy
const parseResult = await linkedinStrategy(coreParser, {
keywords,
locationFilter: LOCATION_FILTER,
maxResults: MAX_RESULTS,
credentials: {
username: LINKEDIN_USERNAME,
password: LINKEDIN_PASSWORD,
},
});
const { results, rejectedResults, summary } = parseResult;
// AI Analysis if enabled
let analysisResults = null;
if (ENABLE_AI_ANALYSIS && results.length > 0) {
logger.step("🧠 Running AI Analysis...");
const ollamaStatus = await checkOllamaStatus();
if (ollamaStatus.available) {
analysisResults = await analyzeBatch(results, {
context:
"LinkedIn posts analysis focusing on job market trends and layoffs",
});
logger.success(`✅ AI Analysis completed for ${results.length} posts`);
} else {
logger.warning("⚠️ Ollama not available, skipping AI analysis");
}
}
// Save results
const outputData = {
metadata: {
extractedAt: new Date().toISOString(),
parser: "linkedin-parser",
version: "2.0.0",
summary,
analysisResults,
},
results,
rejectedResults,
};
const resultsDir = path.join(__dirname, "results");
if (!fs.existsSync(resultsDir)) {
fs.mkdirSync(resultsDir, { recursive: true });
}
const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
const filename = `linkedin-results-${timestamp}.json`;
const filepath = path.join(resultsDir, filename);
fs.writeFileSync(filepath, JSON.stringify(outputData, null, 2));
// Final summary
logger.success("✅ LinkedIn parsing completed successfully!");
logger.info(`📊 Total posts found: ${results.length}`);
logger.info(`❌ Total rejected: ${rejectedResults.length}`);
logger.info(`📁 Results saved to: ${filepath}`);
return outputData;
} catch (error) {
logger.error(`❌ LinkedIn parser failed: ${error.message}`);
throw error;
} finally {
await coreParser.cleanup();
}
}
// CLI handling
if (require.main === module) {
const args = process.argv.slice(2);
const options = {};
// Parse command line arguments
args.forEach((arg) => {
if (arg.startsWith("--")) {
const [key, value] = arg.slice(2).split("=");
options[key] = value || true;
}
});
startLinkedInParser(options)
.then(() => process.exit(0))
.catch((error) => {
console.error("Fatal error:", error.message);
process.exit(1);
});
}
module.exports = { startLinkedInParser };