#!/usr/bin/env node import fs from 'fs'; import path from 'path'; import OpenAI from 'openai'; // Configuration const OPENAI_API_KEY = process.env.OPENAI_API_KEY; const DIST_DIR = './dist'; const OUTPUT_CSV = './category-descriptions.csv'; // Model configuration const MODEL = 'gpt-5.1'; // Initialize OpenAI client const openai = new OpenAI({ apiKey: OPENAI_API_KEY, }); // System prompt for generating SEO descriptions const SEO_DESCRIPTION_PROMPT = `You are given a list of products from a specific category. Create a SEO-friendly description for that category that would be suitable for a product catalog page. Requirements: - Write in German - Make it SEO-optimized with relevant keywords The product list format is: First line: categoryName,categoryId Subsequent lines: articleNumber,price,productName,shortDescription Generate a compelling category description based on this product data.`; // Function to find all *-list.txt files in dist directory function findListFiles() { try { const files = fs.readdirSync(DIST_DIR); return files.filter(file => file.endsWith('-list.txt')); } catch (error) { console.error('Error reading dist directory:', error.message); return []; } } // Function to read a list file and extract category info function readListFile(filePath) { try { const content = fs.readFileSync(filePath, 'utf8'); const lines = content.trim().split('\n'); if (lines.length < 1) { throw new Error('File is empty'); } // Parse first line: categoryName,categoryId const firstLine = lines[0]; const [categoryName, categoryId] = firstLine.split(','); if (!categoryName || !categoryId) { throw new Error('Invalid first line format'); } return { categoryName: categoryName.replace(/^"|"$/g, ''), // Remove quotes if present categoryId: categoryId.replace(/^"|"$/g, ''), content: content }; } catch (error) { console.error(`Error reading ${filePath}:`, error.message); return null; } } // Function to generate SEO description using OpenAI async function generateSEODescription(productListContent, categoryName, categoryId) { try { console.log(`🔄 Generating SEO description for category: ${categoryName} (ID: ${categoryId})`); const response = await openai.responses.create({ model: "gpt-5.1", input: [ { "role": "developer", "content": [ { "type": "input_text", "text": SEO_DESCRIPTION_PROMPT } ] }, { "role": "user", "content": [ { "type": "input_text", "text": productListContent } ] } ], text: { "format": { "type": "json_schema", "name": "descriptions", "strict": true, "schema": { "type": "object", "properties": { "seo_description": { "type": "string", "description": "A concise description intended for SEO purposes. 155 characters" }, "long_description": { "type": "string", "description": "A comprehensive description, 2-5 Sentences" } }, "required": [ "seo_description", "long_description" ], "additionalProperties": false } }, "verbosity": "medium" }, reasoning: { "effort": "none", "summary": "auto" }, tools: [], store: false, include: [ "reasoning.encrypted_content", "web_search_call.action.sources" ] }); const description = response.output_text; console.log(`✅ Generated description for ${categoryName}`); return description; } catch (error) { console.error(`❌ Error generating description for ${categoryName}:`, error.message); return `Error generating description: ${error.message}`; } } // Function to write CSV file function writeCSV(results) { try { const csvHeader = 'categoryId,listFileName,seoDescription\n'; const csvRows = results.map(result => `"${result.categoryId}","${result.listFileName}","${result.description.replace(/"/g, '""')}"` ).join('\n'); const csvContent = csvHeader + csvRows; fs.writeFileSync(OUTPUT_CSV, csvContent, 'utf8'); console.log(`✅ CSV file written: ${OUTPUT_CSV}`); console.log(`📊 Processed ${results.length} categories`); } catch (error) { console.error('Error writing CSV file:', error.message); } } // Main execution function async function main() { console.log('🚀 Starting category description generation...'); // Check if OpenAI API key is set if (!OPENAI_API_KEY) { console.error('❌ OPENAI_API_KEY environment variable is not set'); console.log('Please set your OpenAI API key: export OPENAI_API_KEY="your-api-key-here"'); process.exit(1); } // Check if dist directory exists if (!fs.existsSync(DIST_DIR)) { console.error(`❌ Dist directory not found: ${DIST_DIR}`); process.exit(1); } // Find all list files const listFiles = findListFiles(); if (listFiles.length === 0) { console.log('⚠️ No *-list.txt files found in dist directory'); console.log('💡 Make sure to run the prerender script first to generate the list files'); process.exit(1); } console.log(`📂 Found ${listFiles.length} list files to process`); const results = []; // Process each list file for (const listFile of listFiles) { const filePath = path.join(DIST_DIR, listFile); // Read and parse the file const fileData = readListFile(filePath); if (!fileData) { console.log(`⚠️ Skipping ${listFile} due to read error`); continue; } // Generate SEO description const description = await generateSEODescription( fileData.content, fileData.categoryName, fileData.categoryId ); // Store result results.push({ categoryId: fileData.categoryId, listFileName: listFile, description: description }); // Add delay to avoid rate limiting await new Promise(resolve => setTimeout(resolve, 1000)); } // Write CSV output if (results.length > 0) { writeCSV(results); console.log('🎉 Category description generation completed successfully!'); } else { console.error('❌ No results to write - all files failed processing'); process.exit(1); } } // Run the script if (import.meta.url === `file://${process.argv[1]}`) { main().catch(error => { console.error('❌ Script failed:', error.message); process.exit(1); }); } export { findListFiles, readListFile, generateSEODescription, writeCSV };