metaads-pro/src/app/api/analysis/route.ts

145 lines
5.2 KiB
TypeScript
Raw Normal View History

import { NextRequest, NextResponse } from 'next/server';
import { createMetaMCPClient } from '@/lib/mcp/client';
import { getCampaignDetails } from '@/lib/mcp/tools';
import { getAccessToken } from '@/lib/auth/session';
import { getAIClient } from '@/lib/ai/client';
const ANALYSIS_MODEL = 'gpt-5.4-mini';
export async function POST(request: NextRequest) {
try {
const token = await getAccessToken();
if (!token) {
return NextResponse.json({ success: false, error: 'Not authenticated' }, { status: 401 });
}
const { campaignId } = await request.json();
if (!campaignId) {
return NextResponse.json({ success: false, error: 'Campaign ID required' }, { status: 400 });
}
const client = await createMetaMCPClient(token);
let campaignData: any;
try {
const result = await getCampaignDetails(client, campaignId);
campaignData = Array.isArray(result) ? result[0] : result;
} finally {
await client.close();
}
if (!campaignData || !campaignData.id) {
return NextResponse.json({ success: false, error: 'Campaign not found on Meta' }, { status: 404 });
}
// Prepare data for the AI model
const insights = campaignData.insights?.data?.[0] || {};
const adsets = campaignData.adsets?.data || [];
let totalSpend = parseFloat(insights.spend || '0');
let roas = parseFloat(insights.purchase_roas?.[0]?.value || '0');
// Extract Ads & Creatives
const adsToAnalyze = [];
for (const adset of adsets) {
const ads = adset.ads?.data || [];
for (const ad of ads) {
const adInsights = ad.insights?.data?.[0] || {};
const creative = ad.creative || {};
// Extract texts
let body = creative.body || '';
let title = creative.title || '';
let imageUrl = creative.image_url || creative.thumbnail_url || '';
// Handle asset_feed_spec (Dynamic Creative)
if (creative.asset_feed_spec) {
const feed = creative.asset_feed_spec;
body = feed.bodies?.map((b: any) => b.text).join(' | ') || body;
title = feed.titles?.map((t: any) => t.text).join(' | ') || title;
if (feed.images && feed.images.length > 0) {
imageUrl = feed.images[0].url || feed.images[0].thumbnail_url || imageUrl; // Just take the first image for now
}
}
adsToAnalyze.push({
ad_name: ad.name,
spend: adInsights.spend || '0',
roas: adInsights.purchase_roas?.[0]?.value || '0',
cpc: adInsights.cpc || '0',
ctr: adInsights.ctr || '0',
body,
title,
image_url: imageUrl,
});
}
}
const openai = getAIClient();
const prompt = `
Você é um Especialista Sênior em Tráfego Pago e Copywriting.
Abaixo estão os dados reais extraídos de uma Campanha no Facebook Ads.
Por favor, faça uma auditoria completa.
DADOS DA CAMPANHA:
Nome: ${campaignData.name}
Gasto Total (30d): R$ ${totalSpend}
ROAS Total: ${roas}
ANÚNCIOS ATIVOS (CRIATIVOS E MÉTRICAS):
${JSON.stringify(adsToAnalyze, null, 2)}
Sua tarefa é retornar um JSON ESTRITAMENTE VÁLIDO com a seguinte estrutura de análise (NÃO adicione formatação markdown \`\`\`json no início ou no fim).
Importante: Para as análises e ações, retorne um ARRAY de strings (cada string é um bullet point claro, curto e direto).
{
"diagnostico_geral": "Um parágrafo forte resumindo a saúde da campanha.",
"analise_copy": ["Ponto 1 sobre a copy...", "Sugestão de melhoria de texto..."],
"analise_imagem": ["O que achei do visual...", "Ideia de novo formato (vídeo/carrossel)..."],
"analise_metricas": ["ROAS está X...", "CPA está Y..."],
"acao_recomendada": ["Ação imediata 1 (ex: Pausar Ad X)...", "Ação 2 (ex: Testar público Y)..."],
"nova_estrategia_sugerida": {
"nicho_sugerido": "ex: ecommerce, info_produto, negocios_locais, advogado...",
"produto_foco": "Nome ou tipo do produto que deve ser focado na próxima campanha",
"publico_alvo": "Qual deve ser o público alvo ideal",
"angulos_de_venda": ["Ângulo 1", "Ângulo 2"]
}
}
`;
const completion = await openai.chat.completions.create({
model: ANALYSIS_MODEL,
messages: [{ role: 'user', content: prompt }],
response_format: { type: 'json_object' },
});
const textRes = completion.choices[0]?.message?.content || '';
let jsonRes;
try {
// Clean up markdown fences if model ignored instruction
const cleanedText = textRes.replace(/^```json/m, '').replace(/```$/m, '').trim();
jsonRes = JSON.parse(cleanedText);
} catch (e) {
console.error('Failed to parse AI response:', textRes);
return NextResponse.json({ success: false, error: 'Failed to parse AI response' }, { status: 500 });
}
return NextResponse.json({
success: true,
data: {
campaign_metrics: { spend: totalSpend, roas },
ads_count: adsToAnalyze.length,
ads: adsToAnalyze,
analysis: jsonRes
}
});
} catch (error) {
console.error('[Analysis] API Error:', error);
return NextResponse.json(
{ success: false, error: error instanceof Error ? error.message : 'Unknown error' },
{ status: 500 }
);
}
}