@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
JavaScript
;
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';