import axios from 'axios';
import https from 'https';
import crypto from 'crypto';
import { spawn } from 'child_process';
import dotenv from 'dotenv';
import { generateDadJokeImageWithLocalFallback } from './localImageGenerator.js';
dotenv.config();
// macOS LibreSSL bypass - set global SSL ignore flags
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
process.env.UV_THREADPOOL_SIZE = '128';
// Create HTTPS agent that bypasses SSL certificate issues for Nano-banana
const httpsAgent = new https.Agent({
rejectUnauthorized: false,
// Use TLS 1.3 for modern compatibility (works with macOS LibreSSL)
secureProtocol: 'TLS_method',
checkServerIdentity: () => undefined,
// SSL bypass for macOS LibreSSL compatibility
ciphers: 'ALL:!NULL:!EXPORT:!DES:!RC4:!MD5:!DSS',
honorCipherOrder: false,
// Additional macOS compatibility settings
secureOptions: crypto.constants.SSL_OP_NO_SSLv2 | crypto.constants.SSL_OP_NO_SSLv3
});
// Alternative TLS agent for compatibility
const altHttpsAgent = new https.Agent({
rejectUnauthorized: false,
secureProtocol: 'TLSv1_2_method',
checkServerIdentity: () => undefined,
ciphers: 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384',
honorCipherOrder: false
});
// Ultra-compatible agent with minimal TLS requirements
const compatibleHttpsAgent = new https.Agent({
rejectUnauthorized: false,
secureProtocol: 'TLSv1_3_method',
checkServerIdentity: () => undefined
});
const NANOBANANA_BASE_URL = process.env.NANOBANANA_BASE_URL || 'https://api.nanobanana.com';
const NANOBANANA_API_KEY = process.env.NANOBANANA_API_KEY;
const MCP_HUB_IMAGE_GEN_TOPIC_KEY_AUTH = process.env.MCP_HUB_IMAGE_GEN_TOPIC_KEY_AUTH;
// Nano-banana direct API call with improved SSL handling
async function generateImageWithNanoBananaDirect(joke: string, topic?: string, rating?: string): Promise<string> {
try {
console.log('π Trying Nano-banana direct API call...');
// Create a visual description for the Dad Joke
const imagePrompt = `Create a fun, colorful, cartoon-style illustration that visualizes this Dad Joke: "${joke}".
The image should be family-friendly, bright, and humorous. Include visual elements that represent the joke's pun or wordplay.
Style: clean cartoon illustration with vibrant colors, suitable for all ages.`;
// Try alternative SSL configurations with improved compatibility
const httpsAgentAlt = new https.Agent({
rejectUnauthorized: false,
secureProtocol: 'TLS_method',
checkServerIdentity: () => undefined,
ciphers: 'ALL:!NULL:!EXPORT:!DES:!RC4:!MD5:!DSS',
honorCipherOrder: false,
secureOptions: crypto.constants.SSL_OP_NO_SSLv2 | crypto.constants.SSL_OP_NO_SSLv3
});
const response = await axios.post(
`${NANOBANANA_BASE_URL}/v1/images/generations`,
{
model: 'nano-banana-v1',
prompt: imagePrompt,
n: 1,
size: '1024x1024',
quality: 'standard',
},
{
headers: {
'Authorization': `Bearer ${NANOBANANA_API_KEY}`,
'Content-Type': 'application/json',
'User-Agent': 'DadJokeVisualizer/1.0.0',
'Accept': 'application/json',
},
httpsAgent: httpsAgentAlt,
timeout: 25000,
validateStatus: function (status) {
return status < 500;
},
}
);
const imageUrl = response.data.data?.[0]?.url || response.data?.url || response.data?.image_url;
if (!imageUrl) {
throw new Error('No image URL returned from Nano-banana API');
}
console.log('β
Image generated successfully with Nano-banana direct API!');
return imageUrl;
} catch (error) {
console.error('β Nano-banana direct API error:', error instanceof Error ? error.message : error);
throw error;
}
}
// Free placeholder/image service for Dad Jokes
async function generateImageWithPixabay(joke: string, topic?: string, rating?: string): Promise<string> {
try {
console.log('π¨ Generating personalized Dad Joke image...');
// Extract keywords from the joke for theme selection
const jokeKeywords = joke.toLowerCase()
.replace(/[^\w\s]/g, '')
.split(' ')
.filter(word => word.length > 3)
.slice(0, 2);
// Select theme based on keywords
let theme = 'general';
const keywordsStr = jokeKeywords.join(' ');
if (keywordsStr.includes('cat') || keywordsStr.includes('pet')) theme = 'animals';
else if (keywordsStr.includes('food') || keywordsStr.includes('eat') || keywordsStr.includes('pizza')) theme = 'food';
else if (keywordsStr.includes('bear') || keywordsStr.includes('animal')) theme = 'animals';
else if (keywordsStr.includes('skeleton') || keywordsStr.includes('bone')) theme = 'spooky';
else if (keywordsStr.includes('bicycle') || keywordsStr.includes('wheel')) theme = 'transportation';
else if (keywordsStr.includes('dad') || keywordsStr.includes('family')) theme = 'family';
// Generate themed image URL using placeholder service
const imageUrl = `https://source.unsplash.com/1024x1024/?${theme},funny,colorful&sig=${Math.random().toString(36).substr(2, 9)}`;
console.log(`β
Generated themed image for "${theme}" theme!`);
return imageUrl;
} catch (error) {
console.error('β Placeholder image error:', error instanceof Error ? error.message : error);
throw error;
}
}
// Image-gen backup service
async function generateImageWithImageGen(joke: string, topic?: string, rating?: string): Promise<string> {
try {
if (!MCP_HUB_IMAGE_GEN_TOPIC_KEY_AUTH) {
throw new Error('MCP Hub Image Gen API key not configured');
}
// Create a visual description for the Dad Joke
const imagePrompt = `Create a fun, colorful, cartoon-style illustration that visualizes this Dad Joke: "${joke}".
The image should be family-friendly, bright, and humorous. Include visual elements that represent the joke's pun or wordplay.
Style: clean cartoon illustration with vibrant colors, suitable for all ages.`;
const response = await axios.post(
'https://api.hub.mcp.topia.gg/1.0/topic/generate-image',
{
prompt: imagePrompt,
width: 1024,
height: 1024,
model: 'dall-e-3'
},
{
headers: {
'Authorization': `Bearer ${MCP_HUB_IMAGE_GEN_TOPIC_KEY_AUTH}`,
'Content-Type': 'application/json',
},
timeout: 15000,
}
);
const imageUrl = response.data.url;
if (!imageUrl) {
throw new Error('Failed to generate image with image-gen');
}
console.log('β
Image generated successfully with image-gen!');
return imageUrl;
} catch (error) {
console.error('β Image-gen API error:', error instanceof Error ? error.message : error);
throw error;
}
}
// Advanced Nano-banana caller with complete SSL bypass
async function callNanoBananaWithSSLFallbacks(promptData: any): Promise<string> {
// Create ultra-permissive agents for SSL bypass
const agents = [
httpsAgent,
altHttpsAgent,
new https.Agent({
rejectUnauthorized: false,
secureProtocol: 'TLSv1_method',
checkServerIdentity: () => undefined,
requestCert: false,
}),
new https.Agent({
rejectUnauthorized: false,
secureProtocol: 'SSLv23_method',
checkServerIdentity: () => undefined,
requestCert: false,
})
];
const endpoints = [
`${NANOBANANA_BASE_URL}/v1/images/generations`,
'https://api.nano-banana.com/v1/images/generations',
'https://nanobanana.com/api/v1/images/generations'
];
for (const endpoint of endpoints) {
for (const agent of agents) {
try {
const agentName = agent === httpsAgent ? 'primary' :
agent === altHttpsAgent ? 'alternative' :
agent.options?.secureProtocol === 'TLSv1_method' ? 'legacy-tls' : 'legacy-ssl';
console.log(`π Trying: ${endpoint} with agent ${agentName}`);
const response = await axios.post(endpoint, promptData, {
headers: {
'Authorization': `Bearer ${NANOBANANA_API_KEY}`,
'Content-Type': 'application/json',
'User-Agent': 'DadJokeVisualizer/1.0.0',
'Accept': 'application/json',
},
httpsAgent: agent,
timeout: 25000,
validateStatus: function (status) {
return status < 500;
},
});
// Try multiple possible response formats
const imageUrl = response.data?.data?.[0]?.url ||
response.data?.url ||
response.data?.image_url ||
response.data?.images?.[0]?.url;
if (imageUrl) {
console.log('β
Nano-banana API SUCCESS:', imageUrl.substring(0, 50) + '...');
return imageUrl;
}
throw new Error('No image URL found in response');
} catch (error) {
console.log(`β Failed: ${endpoint} - ${error instanceof Error ? error.message : error}`);
// Continue to next combination
}
}
}
throw new Error('All Nano-banana SSL configurations failed');
}
export async function generateImage(joke: string, topic?: string, rating?: string): Promise<string> {
// Tier 1: Try Nano-banana API with advanced SSL fallbacks
try {
if (!NANOBANANA_API_KEY) {
throw new Error('Nano-banana API key not configured');
}
console.log('π¨ Attempting image generation with Nano-banana API...');
// Create a visual description for the Dad Joke
const imagePrompt = `Create a fun, colorful, cartoon-style illustration that visualizes this Dad Joke: "${joke}".
The image should be family-friendly, bright, and humorous. Include visual elements that represent the joke's pun or wordplay.
Style: clean cartoon illustration with vibrant colors, suitable for all ages.`;
console.log(`π Connecting to: ${NANOBANANA_BASE_URL}/v1/images/generations`);
console.log(`π Using API Key: ${NANOBANANA_API_KEY ? NANOBANANA_API_KEY.substring(0, 10) + '...' : 'NOT_SET'}`);
const promptData = {
model: 'nano-banana-v1',
prompt: imagePrompt,
n: 1,
size: '1024x1024',
quality: 'standard',
};
const imageUrl = await callNanoBananaWithSSLFallbacks(promptData);
console.log('β
Image generated successfully with Nano-banana!');
return imageUrl;
} catch (error) {
console.error('β Nano-banana API error:', error instanceof Error ? error.message : error);
console.log('π¨ Nano-banana unavailable - using enhanced creative SVG generation!');
console.log('π‘ This provides better creativity than simple placeholder images');
// Prioritize enhanced SVG generation over generic external APIs
console.log('β
Generating enhanced creative visualization instead!');
return generateDadJokeImageWithLocalFallback(joke, topic || 'general', rating || 'PG-13');
}
}