UNPKG

@agentdao/core

Version:

Core functionality, skills, and ready-made UI components for AgentDAO - Web3 subscriptions, content generation, social media, help support, live chat, RSS fetching, web search, and agent pricing integration

607 lines (586 loc) 25.3 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.ContentGenerators = exports.ContentValidationError = exports.APIKeyMissingError = exports.ContentGenerationError = void 0; const axios_1 = __importDefault(require("axios")); // Custom error classes class ContentGenerationError extends Error { constructor(message, originalError) { super(message); this.originalError = originalError; this.name = 'ContentGenerationError'; } } exports.ContentGenerationError = ContentGenerationError; class APIKeyMissingError extends Error { constructor(service) { super(`API key missing for ${service}`); this.name = 'APIKeyMissingError'; } } exports.APIKeyMissingError = APIKeyMissingError; class ContentValidationError extends Error { constructor(message) { super(message); this.name = 'ContentValidationError'; } } exports.ContentValidationError = ContentValidationError; // Content generation utilities class ContentGenerators { /** * Generate blog post content */ static async generateBlogPost(topic, keywords = [], options = {}) { const { length = 'medium', tone = 'professional', includeSEO = true } = options; const openaiApiKey = process.env.OPENAI_API_KEY; if (!openaiApiKey) { throw new APIKeyMissingError('OpenAI'); } try { const wordCount = length === 'short' ? 500 : length === 'medium' ? 1000 : 2000; const tonePrompt = this.getTonePrompt(tone); const prompt = `Write a ${length} blog post about "${topic}"${keywords.length > 0 ? ` incorporating these keywords: ${keywords.join(', ')}` : ''}. ${tonePrompt} Requirements: - Word count: ${wordCount} words - Include an engaging introduction - Use clear headings and subheadings - Provide valuable insights and actionable tips - End with a compelling conclusion - ${includeSEO ? 'Optimize for SEO with natural keyword integration' : ''} IMPORTANT: Respond ONLY with valid JSON. Do not include any text before or after the JSON object. { "title": "Blog post title", "sections": ["Introduction", "Main content sections..."], "content": "Full blog post content with proper escaping for newlines and quotes", "summary": "2-3 sentence summary", "keywords": ["keyword1", "keyword2"], "readingTime": estimated minutes }`; const response = await axios_1.default.post(this.OPENAI_API, { model: 'gpt-4', messages: [ { role: 'system', content: 'You are a professional content writer. Always respond with valid JSON only. Escape any special characters in strings properly.' }, { role: 'user', content: prompt } ], max_tokens: 3000, temperature: 0.7 }, { headers: { 'Authorization': `Bearer ${openaiApiKey}`, 'Content-Type': 'application/json' } }); const content = response.data.choices[0]?.message?.content || ''; if (!content) { throw new ContentGenerationError('No content received from OpenAI API'); } // Clean the content before parsing const cleanedContent = this.cleanJsonContent(content); let blogData; try { blogData = JSON.parse(cleanedContent); } catch (parseError) { // Fallback: try to extract JSON from the response const jsonMatch = cleanedContent.match(/\{[\s\S]*\}/); if (jsonMatch) { try { blogData = JSON.parse(jsonMatch[0]); } catch (fallbackError) { // If all parsing fails, create a fallback structure blogData = { title: `Blog Post: ${topic}`, content: cleanedContent, summary: `A blog post about ${topic}`, keywords: keywords, sections: ['Introduction', 'Main Content', 'Conclusion'], readingTime: Math.ceil(wordCount / 200) }; } } else { // If no JSON found, create a fallback structure blogData = { title: `Blog Post: ${topic}`, content: cleanedContent, summary: `A blog post about ${topic}`, keywords: keywords, sections: ['Introduction', 'Main Content', 'Conclusion'], readingTime: Math.ceil(wordCount / 200) }; } } return { id: `blog_${Date.now()}`, type: 'blog', title: blogData.title || `Blog Post: ${topic}`, content: blogData.content || cleanedContent, summary: blogData.summary, keywords: blogData.keywords || keywords, sections: blogData.sections || ['Introduction', 'Main Content', 'Conclusion'], readingTime: blogData.readingTime || Math.ceil(wordCount / 200), seoOptimized: includeSEO, metadata: { topic, length, tone, wordCount }, timestamp: new Date().toISOString() }; } catch (error) { if (error instanceof ContentGenerationError || error instanceof APIKeyMissingError) { throw error; } if (axios_1.default.isAxiosError(error)) { throw new ContentGenerationError(`OpenAI API request failed: ${error.response?.status} ${error.response?.statusText}`, error); } throw new ContentGenerationError(`Failed to generate blog post: ${error instanceof Error ? error.message : 'Unknown error'}`, error); } } /** * Generate social media post */ static async generateSocialPost(platform, topic, options = {}) { const { tone = 'engaging', includeHashtags = true } = options; const openaiApiKey = process.env.OPENAI_API_KEY; if (!openaiApiKey) { throw new APIKeyMissingError('OpenAI'); } try { const platformLimits = { twitter: 280, linkedin: 3000, facebook: 63206, instagram: 2200 }; const characterLimit = platformLimits[platform]; const tonePrompt = this.getTonePrompt(tone); const prompt = `Create a ${platform} post about "${topic}". ${tonePrompt} Requirements: - Character limit: ${characterLimit} characters - Platform-specific best practices for ${platform} - ${includeHashtags ? 'Include relevant hashtags' : 'No hashtags'} - Engaging and shareable content - Clear call-to-action if appropriate IMPORTANT: Respond ONLY with valid JSON. Do not include any text before or after the JSON object. { "content": "Post content with proper escaping for newlines and quotes", "hashtags": ["hashtag1", "hashtag2"], "characterCount": actual character count }`; const response = await axios_1.default.post(this.OPENAI_API, { model: 'gpt-4', messages: [ { role: 'system', content: `You are a social media expert specializing in ${platform} content creation. Always respond with valid JSON only. Escape any special characters in strings properly.` }, { role: 'user', content: prompt } ], max_tokens: 500, temperature: 0.8 }, { headers: { 'Authorization': `Bearer ${openaiApiKey}`, 'Content-Type': 'application/json' } }); const content = response.data.choices[0]?.message?.content || ''; if (!content) { throw new ContentGenerationError('No content received from OpenAI API'); } // Clean the content before parsing const cleanedContent = this.cleanJsonContent(content); let postData; try { postData = JSON.parse(cleanedContent); } catch (parseError) { // Fallback: try to extract JSON from the response const jsonMatch = cleanedContent.match(/\{[\s\S]*\}/); if (jsonMatch) { try { postData = JSON.parse(jsonMatch[0]); } catch (fallbackError) { // If all parsing fails, create a fallback structure postData = { content: cleanedContent, hashtags: [], characterCount: cleanedContent.length }; } } else { // If no JSON found, create a fallback structure postData = { content: cleanedContent, hashtags: [], characterCount: cleanedContent.length }; } } return { id: `social_${Date.now()}`, type: 'social', title: `${platform} Post: ${topic}`, content: postData.content || content, platform, characterCount: postData.characterCount || content.length, hashtags: postData.hashtags || [], metadata: { topic, tone, platform }, timestamp: new Date().toISOString() }; } catch (error) { if (error instanceof ContentGenerationError || error instanceof APIKeyMissingError) { throw error; } if (axios_1.default.isAxiosError(error)) { throw new ContentGenerationError(`OpenAI API request failed: ${error.response?.status} ${error.response?.statusText}`, error); } throw new ContentGenerationError(`Failed to generate social post: ${error instanceof Error ? error.message : 'Unknown error'}`, error); } } /** * Generate email content */ static async generateEmail(purpose, recipient, options = {}) { const { tone = 'professional', length = 'medium' } = options; const openaiApiKey = process.env.OPENAI_API_KEY; if (!openaiApiKey) { throw new APIKeyMissingError('OpenAI'); } try { const tonePrompt = this.getTonePrompt(tone); const prompt = `Write a ${length} email for the purpose of "${purpose}" to ${recipient}. ${tonePrompt} Requirements: - Professional email format - Clear subject line - Appropriate greeting and signature - Concise and clear message - Professional tone IMPORTANT: Respond ONLY with valid JSON. Do not include any text before or after the JSON object. { "subject": "Email subject line", "greeting": "Dear [Name]", "content": "Email body content with proper escaping for newlines and quotes", "signature": "Best regards, [Name]" }`; const response = await axios_1.default.post(this.OPENAI_API, { model: 'gpt-4', messages: [ { role: 'system', content: 'You are a professional email writer who creates clear, effective business communications. Always respond with valid JSON only. Escape any special characters in strings properly.' }, { role: 'user', content: prompt } ], max_tokens: 1000, temperature: 0.6 }, { headers: { 'Authorization': `Bearer ${openaiApiKey}`, 'Content-Type': 'application/json' } }); const content = response.data.choices[0]?.message?.content || ''; if (!content) { throw new ContentGenerationError('No content received from OpenAI API'); } // Clean the content before parsing const cleanedContent = this.cleanJsonContent(content); let emailData; try { emailData = JSON.parse(cleanedContent); } catch (parseError) { // Fallback: try to extract JSON from the response const jsonMatch = cleanedContent.match(/\{[\s\S]*\}/); if (jsonMatch) { try { emailData = JSON.parse(jsonMatch[0]); } catch (fallbackError) { // If all parsing fails, create a fallback structure emailData = { subject: `Re: ${purpose}`, greeting: 'Dear [Name],', content: cleanedContent, signature: 'Best regards,' }; } } else { // If no JSON found, create a fallback structure emailData = { subject: `Re: ${purpose}`, greeting: 'Dear [Name],', content: cleanedContent, signature: 'Best regards,' }; } } return { id: `email_${Date.now()}`, type: 'email', title: `Email: ${purpose}`, content: emailData.content || content, subject: emailData.subject || `Re: ${purpose}`, greeting: emailData.greeting || 'Dear [Name],', signature: emailData.signature || 'Best regards,', metadata: { purpose, recipient, tone, length }, timestamp: new Date().toISOString() }; } catch (error) { if (error instanceof ContentGenerationError || error instanceof APIKeyMissingError) { throw error; } if (axios_1.default.isAxiosError(error)) { throw new ContentGenerationError(`OpenAI API request failed: ${error.response?.status} ${error.response?.statusText}`, error); } throw new ContentGenerationError(`Failed to generate email: ${error instanceof Error ? error.message : 'Unknown error'}`, error); } } /** * Optimize content for SEO */ static async optimizeContent(content, keywords) { const openaiApiKey = process.env.OPENAI_API_KEY; if (!openaiApiKey) { throw new APIKeyMissingError('OpenAI'); } try { const prompt = `Optimize this content for SEO using the keywords: ${keywords.join(', ')} Original content: ${content} Provide: 1. Optimized version of the content 2. List of improvements made 3. SEO score (1-100) IMPORTANT: Respond ONLY with valid JSON. Do not include any text before or after the JSON object. { "optimized": "Optimized content with proper escaping for newlines and quotes", "improvements": ["Improvement 1", "Improvement 2"], "seoScore": 85 }`; const response = await axios_1.default.post(this.OPENAI_API, { model: 'gpt-4', messages: [ { role: 'system', content: 'You are an SEO expert who optimizes content for better search engine rankings. Always respond with valid JSON only. Escape any special characters in strings properly.' }, { role: 'user', content: prompt } ], max_tokens: 2000, temperature: 0.3 }, { headers: { 'Authorization': `Bearer ${openaiApiKey}`, 'Content-Type': 'application/json' } }); const aiContent = response.data.choices[0]?.message?.content || ''; if (!aiContent) { throw new ContentGenerationError('No content received from OpenAI API'); } // Clean the content before parsing const cleanedContent = this.cleanJsonContent(aiContent); let optimizationData; try { optimizationData = JSON.parse(cleanedContent); } catch (parseError) { // Fallback: try to extract JSON from the response const jsonMatch = cleanedContent.match(/\{[\s\S]*\}/); if (jsonMatch) { try { optimizationData = JSON.parse(jsonMatch[0]); } catch (fallbackError) { // If all parsing fails, create a fallback structure optimizationData = { optimized: cleanedContent, improvements: ['Content optimization completed'], seoScore: 75 }; } } else { // If no JSON found, create a fallback structure optimizationData = { optimized: cleanedContent, improvements: ['Content optimization completed'], seoScore: 75 }; } } return { original: content, optimized: optimizationData.optimized || aiContent, improvements: optimizationData.improvements || ['Content optimization completed'], seoScore: optimizationData.seoScore || 75 }; } catch (error) { if (error instanceof ContentGenerationError || error instanceof APIKeyMissingError) { throw error; } if (axios_1.default.isAxiosError(error)) { throw new ContentGenerationError(`OpenAI API request failed: ${error.response?.status} ${error.response?.statusText}`, error); } throw new ContentGenerationError(`Failed to optimize content: ${error instanceof Error ? error.message : 'Unknown error'}`, error); } } /** * Generate meta tags for SEO */ static async generateMetaTags(title, content) { const openaiApiKey = process.env.OPENAI_API_KEY; if (!openaiApiKey) { throw new APIKeyMissingError('OpenAI'); } try { const prompt = `Generate SEO meta tags for this content: Title: ${title} Content: ${content.substring(0, 500)}... Generate: 1. Meta title (50-60 characters) 2. Meta description (150-160 characters) 3. Keywords (5-10 relevant keywords) 4. Open Graph title and description IMPORTANT: Respond ONLY with valid JSON. Do not include any text before or after the JSON object. { "title": "Meta title", "description": "Meta description", "keywords": ["keyword1", "keyword2"], "ogTitle": "Open Graph title", "ogDescription": "Open Graph description" }`; const response = await axios_1.default.post(this.OPENAI_API, { model: 'gpt-4', messages: [ { role: 'system', content: 'You are an SEO expert who creates effective meta tags for web content. Always respond with valid JSON only. Escape any special characters in strings properly.' }, { role: 'user', content: prompt } ], max_tokens: 500, temperature: 0.3 }, { headers: { 'Authorization': `Bearer ${openaiApiKey}`, 'Content-Type': 'application/json' } }); const aiContent = response.data.choices[0]?.message?.content || ''; if (!aiContent) { throw new ContentGenerationError('No content received from OpenAI API'); } // Clean the content before parsing const cleanedContent = this.cleanJsonContent(aiContent); let metaData; try { metaData = JSON.parse(cleanedContent); } catch (parseError) { // Fallback: try to extract JSON from the response const jsonMatch = cleanedContent.match(/\{[\s\S]*\}/); if (jsonMatch) { try { metaData = JSON.parse(jsonMatch[0]); } catch (fallbackError) { // If all parsing fails, create a fallback structure metaData = { title: title.substring(0, 60), description: content.substring(0, 160), keywords: [], ogTitle: title, ogDescription: content.substring(0, 160) }; } } else { // If no JSON found, create a fallback structure metaData = { title: title.substring(0, 60), description: content.substring(0, 160), keywords: [], ogTitle: title, ogDescription: content.substring(0, 160) }; } } return { title: metaData.title || title, description: metaData.description || aiContent.substring(0, 160), keywords: metaData.keywords || [], ogTitle: metaData.ogTitle || title, ogDescription: metaData.ogDescription || aiContent.substring(0, 160) }; } catch (error) { if (error instanceof ContentGenerationError || error instanceof APIKeyMissingError) { throw error; } if (axios_1.default.isAxiosError(error)) { throw new ContentGenerationError(`OpenAI API request failed: ${error.response?.status} ${error.response?.statusText}`, error); } throw new ContentGenerationError(`Failed to generate meta tags: ${error instanceof Error ? error.message : 'Unknown error'}`, error); } } // Helper methods static getTonePrompt(tone) { switch (tone) { case 'professional': return 'Use a professional, business-like tone with formal language and industry terminology.'; case 'casual': return 'Use a casual, conversational tone that feels friendly and approachable.'; case 'technical': return 'Use a technical tone with precise terminology and detailed explanations.'; case 'engaging': return 'Use an engaging, compelling tone that encourages interaction and sharing.'; case 'friendly': return 'Use a warm, friendly tone that builds rapport and trust.'; default: return 'Use a balanced, professional tone.'; } } static cleanJsonContent(content) { // Remove any leading/trailing whitespace and newlines let cleaned = content.trim(); // Remove any markdown code blocks if present cleaned = cleaned.replace(/```json\s*/g, '').replace(/```\s*$/g, ''); // Remove any text before the first { const jsonStart = cleaned.indexOf('{'); if (jsonStart > 0) { cleaned = cleaned.substring(jsonStart); } // Remove any text after the last } const jsonEnd = cleaned.lastIndexOf('}'); if (jsonEnd > 0 && jsonEnd < cleaned.length - 1) { cleaned = cleaned.substring(0, jsonEnd + 1); } // Replace problematic control characters cleaned = cleaned .replace(/\n/g, '\\n') // Escape newlines .replace(/\r/g, '\\r') // Escape carriage returns .replace(/\t/g, '\\t') // Escape tabs .replace(/\f/g, '\\f') // Escape form feeds .replace(/\b/g, '\\b') // Escape backspaces .replace(/\v/g, '\\v'); // Escape vertical tabs return cleaned; } } exports.ContentGenerators = ContentGenerators; ContentGenerators.OPENAI_API = 'https://api.openai.com/v1/chat/completions';