615 lines
16 KiB
TypeScript
615 lines
16 KiB
TypeScript
|
|
// ============================================
|
||
|
|
// MCP Tools — Typed wrappers for Meta MCP tools
|
||
|
|
// ============================================
|
||
|
|
|
||
|
|
import { callMetaTool, type Client } from './client';
|
||
|
|
|
||
|
|
// ---- Helper: extract text content from MCP result ----
|
||
|
|
function extractContent(result: Awaited<ReturnType<typeof callMetaTool>>): unknown {
|
||
|
|
if (!result.content || !Array.isArray(result.content)) return null;
|
||
|
|
|
||
|
|
const textPart = result.content.find((c: { type: string }) => c.type === 'text');
|
||
|
|
if (!textPart || !('text' in textPart)) return null;
|
||
|
|
|
||
|
|
try {
|
||
|
|
return JSON.parse(textPart.text as string);
|
||
|
|
} catch {
|
||
|
|
return textPart.text;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
function requireId(value: unknown, label: string) {
|
||
|
|
if (!value || typeof value !== 'string') {
|
||
|
|
throw new Error(`${label} obrigatório não informado`);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
function getDefaultOptimizationGoal(objective: string, destination?: string) {
|
||
|
|
if (destination === 'WHATSAPP') {
|
||
|
|
return 'CONVERSATIONS';
|
||
|
|
}
|
||
|
|
|
||
|
|
switch (objective) {
|
||
|
|
case 'OUTCOME_TRAFFIC':
|
||
|
|
return 'LINK_CLICKS';
|
||
|
|
|
||
|
|
case 'OUTCOME_AWARENESS':
|
||
|
|
return 'REACH';
|
||
|
|
|
||
|
|
case 'OUTCOME_ENGAGEMENT':
|
||
|
|
return 'POST_ENGAGEMENT';
|
||
|
|
|
||
|
|
case 'OUTCOME_LEADS':
|
||
|
|
return 'LEAD_GENERATION';
|
||
|
|
|
||
|
|
case 'OUTCOME_SALES':
|
||
|
|
// OFFSITE_CONVERSIONS requires a pixel_id. Since we don't have it in MVP, fallback to LINK_CLICKS
|
||
|
|
return 'LINK_CLICKS';
|
||
|
|
|
||
|
|
default:
|
||
|
|
return 'LINK_CLICKS';
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
function getDefaultBillingEvent(objective: string) {
|
||
|
|
switch (objective) {
|
||
|
|
case 'OUTCOME_TRAFFIC':
|
||
|
|
case 'OUTCOME_AWARENESS':
|
||
|
|
case 'OUTCOME_ENGAGEMENT':
|
||
|
|
case 'OUTCOME_LEADS':
|
||
|
|
case 'OUTCOME_SALES':
|
||
|
|
default:
|
||
|
|
return 'IMPRESSIONS';
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
function normalizeBidCapParams(params: {
|
||
|
|
bid_strategy?: string;
|
||
|
|
bid_amount?: number;
|
||
|
|
}) {
|
||
|
|
if (!params.bid_strategy && !params.bid_amount) return {};
|
||
|
|
|
||
|
|
const bidStrategy = params.bid_strategy || 'LOWEST_COST_WITH_BID_CAP';
|
||
|
|
|
||
|
|
if (bidStrategy === 'LOWEST_COST_WITH_BID_CAP' && !params.bid_amount) {
|
||
|
|
throw new Error('Para campanha Bid Cap, informe bid_amount em centavos. Exemplo: R$10,00 = 1000.');
|
||
|
|
}
|
||
|
|
|
||
|
|
return {
|
||
|
|
bid_strategy: bidStrategy,
|
||
|
|
bid_amount: params.bid_amount,
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
// ==============================
|
||
|
|
// Campaign Management
|
||
|
|
// ==============================
|
||
|
|
|
||
|
|
export async function listCampaigns(client: Client, accountId?: string, datePreset: string = 'last_30d') {
|
||
|
|
const args: Record<string, unknown> = { date_preset: datePreset };
|
||
|
|
if (accountId) args.ad_account_id = accountId;
|
||
|
|
|
||
|
|
const result = await callMetaTool(client, 'list_campaigns', args);
|
||
|
|
return extractContent(result);
|
||
|
|
}
|
||
|
|
|
||
|
|
export async function getCampaignDetails(
|
||
|
|
client: Client,
|
||
|
|
campaignId: string,
|
||
|
|
datePreset: string = 'last_30d'
|
||
|
|
) {
|
||
|
|
requireId(campaignId, 'campaignId');
|
||
|
|
|
||
|
|
const result = await callMetaTool(client, 'get_campaign_details', {
|
||
|
|
campaign_id: campaignId,
|
||
|
|
date_preset: datePreset,
|
||
|
|
});
|
||
|
|
|
||
|
|
return extractContent(result);
|
||
|
|
}
|
||
|
|
|
||
|
|
export async function pauseCampaign(client: Client, campaignId: string) {
|
||
|
|
requireId(campaignId, 'campaignId');
|
||
|
|
|
||
|
|
const result = await callMetaTool(client, 'update_campaign', {
|
||
|
|
campaign_id: campaignId,
|
||
|
|
status: 'PAUSED',
|
||
|
|
});
|
||
|
|
|
||
|
|
return extractContent(result);
|
||
|
|
}
|
||
|
|
|
||
|
|
export async function resumeCampaign(client: Client, campaignId: string) {
|
||
|
|
requireId(campaignId, 'campaignId');
|
||
|
|
|
||
|
|
const result = await callMetaTool(client, 'update_campaign', {
|
||
|
|
campaign_id: campaignId,
|
||
|
|
status: 'ACTIVE',
|
||
|
|
});
|
||
|
|
|
||
|
|
return extractContent(result);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Mantido por compatibilidade.
|
||
|
|
* ATENÇÃO: orçamento normalmente é no AdSet, não na campanha.
|
||
|
|
* Para atualizar orçamento real, prefira updateAdSetBudget().
|
||
|
|
*/
|
||
|
|
export async function updateCampaignBudget(
|
||
|
|
client: Client,
|
||
|
|
campaignId: string,
|
||
|
|
dailyBudget: number
|
||
|
|
) {
|
||
|
|
requireId(campaignId, 'campaignId');
|
||
|
|
|
||
|
|
throw new Error(
|
||
|
|
`updateCampaignBudget não deve atualizar daily_budget direto na campanha.
|
||
|
|
Use listAdSets(client, campaignId), pegue o adset_id e chame updateAdSetBudget(client, adsetId, dailyBudget).`
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
// ==============================
|
||
|
|
// Insights & Analytics
|
||
|
|
// ==============================
|
||
|
|
|
||
|
|
export async function getCampaignInsights(
|
||
|
|
client: Client,
|
||
|
|
campaignId: string,
|
||
|
|
datePreset: string = 'last_7d'
|
||
|
|
) {
|
||
|
|
requireId(campaignId, 'campaignId');
|
||
|
|
|
||
|
|
const result = await callMetaTool(client, 'get_insights_for_campaign', {
|
||
|
|
campaign_id: campaignId,
|
||
|
|
date_preset: datePreset,
|
||
|
|
});
|
||
|
|
|
||
|
|
return extractContent(result);
|
||
|
|
}
|
||
|
|
|
||
|
|
export async function getAccountInsights(
|
||
|
|
client: Client,
|
||
|
|
datePreset: string = 'last_7d',
|
||
|
|
accountId?: string
|
||
|
|
) {
|
||
|
|
const args: Record<string, unknown> = {
|
||
|
|
date_preset: datePreset,
|
||
|
|
};
|
||
|
|
|
||
|
|
if (accountId) args.ad_account_id = accountId;
|
||
|
|
|
||
|
|
const result = await callMetaTool(client, 'get_insights_for_account', args);
|
||
|
|
return extractContent(result);
|
||
|
|
}
|
||
|
|
|
||
|
|
export async function getAdAccounts(client: Client) {
|
||
|
|
const result = await callMetaTool(client, 'list_ad_accounts');
|
||
|
|
return extractContent(result);
|
||
|
|
}
|
||
|
|
|
||
|
|
export async function getPages(client: Client) {
|
||
|
|
const result = await callMetaTool(client, 'list_pages');
|
||
|
|
return extractContent(result);
|
||
|
|
}
|
||
|
|
|
||
|
|
// ==============================
|
||
|
|
// Ad Set Management
|
||
|
|
// ==============================
|
||
|
|
|
||
|
|
export async function listAdSets(client: Client, campaignId: string) {
|
||
|
|
requireId(campaignId, 'campaignId');
|
||
|
|
|
||
|
|
const result = await callMetaTool(client, 'list_adsets', {
|
||
|
|
campaign_id: campaignId,
|
||
|
|
});
|
||
|
|
|
||
|
|
return extractContent(result);
|
||
|
|
}
|
||
|
|
|
||
|
|
export async function updateAdSetBudget(
|
||
|
|
client: Client,
|
||
|
|
adSetId: string,
|
||
|
|
dailyBudget: number
|
||
|
|
) {
|
||
|
|
requireId(adSetId, 'adSetId');
|
||
|
|
|
||
|
|
const result = await callMetaTool(client, 'update_adset', {
|
||
|
|
adset_id: adSetId,
|
||
|
|
daily_budget: dailyBudget,
|
||
|
|
});
|
||
|
|
|
||
|
|
return extractContent(result);
|
||
|
|
}
|
||
|
|
|
||
|
|
export async function pauseAdSet(client: Client, adSetId: string) {
|
||
|
|
requireId(adSetId, 'adSetId');
|
||
|
|
|
||
|
|
const result = await callMetaTool(client, 'update_adset', {
|
||
|
|
adset_id: adSetId,
|
||
|
|
status: 'PAUSED',
|
||
|
|
});
|
||
|
|
|
||
|
|
return extractContent(result);
|
||
|
|
}
|
||
|
|
|
||
|
|
export async function resumeAdSet(client: Client, adSetId: string) {
|
||
|
|
requireId(adSetId, 'adSetId');
|
||
|
|
|
||
|
|
const result = await callMetaTool(client, 'update_adset', {
|
||
|
|
adset_id: adSetId,
|
||
|
|
status: 'ACTIVE',
|
||
|
|
});
|
||
|
|
|
||
|
|
return extractContent(result);
|
||
|
|
}
|
||
|
|
|
||
|
|
// ==============================
|
||
|
|
// Campaign Creation
|
||
|
|
// ==============================
|
||
|
|
|
||
|
|
export async function createCampaign(
|
||
|
|
client: Client,
|
||
|
|
params: {
|
||
|
|
name: string;
|
||
|
|
objective: string;
|
||
|
|
status?: string;
|
||
|
|
ad_account_id?: string;
|
||
|
|
}
|
||
|
|
) {
|
||
|
|
const result = await callMetaTool(client, 'create_campaign', {
|
||
|
|
name: params.name,
|
||
|
|
objective: params.objective,
|
||
|
|
status: params.status || 'PAUSED',
|
||
|
|
ad_account_id: params.ad_account_id,
|
||
|
|
});
|
||
|
|
|
||
|
|
return extractContent(result);
|
||
|
|
}
|
||
|
|
|
||
|
|
export async function createAdSet(
|
||
|
|
client: Client,
|
||
|
|
params: {
|
||
|
|
name: string;
|
||
|
|
campaign_id: string;
|
||
|
|
daily_budget: number;
|
||
|
|
targeting: Record<string, unknown>;
|
||
|
|
objective: string;
|
||
|
|
optimization_goal?: string;
|
||
|
|
billing_event?: string;
|
||
|
|
status?: string;
|
||
|
|
promoted_object?: Record<string, unknown>;
|
||
|
|
destination_type?: string;
|
||
|
|
bid_strategy?: string;
|
||
|
|
bid_amount?: number;
|
||
|
|
start_time?: string;
|
||
|
|
end_time?: string;
|
||
|
|
ad_account_id?: string;
|
||
|
|
}
|
||
|
|
) {
|
||
|
|
const bidCapParams: any = {};
|
||
|
|
if (params.bid_strategy) bidCapParams['bid_strategy'] = params.bid_strategy;
|
||
|
|
if (params.bid_amount) bidCapParams['bid_amount'] = params.bid_amount;
|
||
|
|
|
||
|
|
const result = await callMetaTool(client, 'create_adset', {
|
||
|
|
name: params.name,
|
||
|
|
campaign_id: params.campaign_id,
|
||
|
|
daily_budget: params.daily_budget,
|
||
|
|
targeting: params.targeting,
|
||
|
|
objective: params.objective,
|
||
|
|
optimization_goal:
|
||
|
|
params.optimization_goal ||
|
||
|
|
getDefaultOptimizationGoal(params.objective, params.destination_type),
|
||
|
|
billing_event: params.billing_event || getDefaultBillingEvent(params.objective),
|
||
|
|
status: params.status || 'PAUSED',
|
||
|
|
promoted_object: params.promoted_object,
|
||
|
|
destination_type: params.destination_type,
|
||
|
|
start_time: params.start_time,
|
||
|
|
end_time: params.end_time,
|
||
|
|
ad_account_id: params.ad_account_id,
|
||
|
|
...bidCapParams,
|
||
|
|
});
|
||
|
|
|
||
|
|
return extractContent(result);
|
||
|
|
}
|
||
|
|
|
||
|
|
export async function createAd(
|
||
|
|
client: Client,
|
||
|
|
params: {
|
||
|
|
name: string;
|
||
|
|
adset_id: string;
|
||
|
|
creative_id: string;
|
||
|
|
status?: string;
|
||
|
|
ad_account_id?: string;
|
||
|
|
}
|
||
|
|
) {
|
||
|
|
const result = await callMetaTool(client, 'create_ad', {
|
||
|
|
name: params.name,
|
||
|
|
adset_id: params.adset_id,
|
||
|
|
creative_id: params.creative_id,
|
||
|
|
status: params.status || 'PAUSED',
|
||
|
|
ad_account_id: params.ad_account_id,
|
||
|
|
});
|
||
|
|
|
||
|
|
return extractContent(result);
|
||
|
|
}
|
||
|
|
|
||
|
|
export async function createAdCreative(
|
||
|
|
client: Client,
|
||
|
|
params: {
|
||
|
|
name: string;
|
||
|
|
page_id: string;
|
||
|
|
instagram_actor_id?: string;
|
||
|
|
image_hash?: string;
|
||
|
|
image_hashes?: string[];
|
||
|
|
link: string;
|
||
|
|
message: string;
|
||
|
|
headline: string;
|
||
|
|
description?: string;
|
||
|
|
call_to_action_type?: string;
|
||
|
|
ad_account_id?: string;
|
||
|
|
}
|
||
|
|
) {
|
||
|
|
const result = await callMetaTool(client, 'create_ad_creative', {
|
||
|
|
name: params.name,
|
||
|
|
page_id: params.page_id,
|
||
|
|
instagram_actor_id: params.instagram_actor_id,
|
||
|
|
image_hash: params.image_hash,
|
||
|
|
image_hashes: params.image_hashes,
|
||
|
|
link: params.link,
|
||
|
|
message: params.message,
|
||
|
|
headline: params.headline,
|
||
|
|
description: params.description || '',
|
||
|
|
call_to_action_type: params.call_to_action_type || 'LEARN_MORE',
|
||
|
|
ad_account_id: params.ad_account_id,
|
||
|
|
});
|
||
|
|
|
||
|
|
return extractContent(result);
|
||
|
|
}
|
||
|
|
|
||
|
|
export async function createWhatsappAdCreative(
|
||
|
|
client: Client,
|
||
|
|
params: {
|
||
|
|
name: string;
|
||
|
|
page_id: string;
|
||
|
|
instagram_actor_id?: string;
|
||
|
|
image_hash?: string;
|
||
|
|
image_hashes?: string[];
|
||
|
|
message: string;
|
||
|
|
headline: string;
|
||
|
|
description?: string;
|
||
|
|
link?: string;
|
||
|
|
whatsapp_link?: string;
|
||
|
|
whatsapp_phone_number?: string;
|
||
|
|
ad_account_id?: string;
|
||
|
|
}
|
||
|
|
) {
|
||
|
|
const result = await callMetaTool(client, 'create_whatsapp_ad_creative', {
|
||
|
|
name: params.name,
|
||
|
|
page_id: params.page_id,
|
||
|
|
instagram_actor_id: params.instagram_actor_id,
|
||
|
|
image_hash: params.image_hash,
|
||
|
|
image_hashes: params.image_hashes,
|
||
|
|
message: params.message,
|
||
|
|
headline: params.headline,
|
||
|
|
description: params.description || '',
|
||
|
|
link: params.link,
|
||
|
|
whatsapp_link: params.whatsapp_link,
|
||
|
|
whatsapp_phone_number: params.whatsapp_phone_number,
|
||
|
|
ad_account_id: params.ad_account_id,
|
||
|
|
});
|
||
|
|
|
||
|
|
return extractContent(result);
|
||
|
|
}
|
||
|
|
|
||
|
|
export async function uploadAdImage(
|
||
|
|
client: Client,
|
||
|
|
imageUrl?: string,
|
||
|
|
imageBytes?: string,
|
||
|
|
adAccountId?: string
|
||
|
|
) {
|
||
|
|
const params: Record<string, string> = {};
|
||
|
|
|
||
|
|
if (imageUrl) params.image_url = imageUrl;
|
||
|
|
if (imageBytes) params.image_bytes = imageBytes;
|
||
|
|
if (adAccountId) params.ad_account_id = adAccountId;
|
||
|
|
|
||
|
|
const result = await callMetaTool(client, 'upload_ad_image', params);
|
||
|
|
return extractContent(result);
|
||
|
|
}
|
||
|
|
|
||
|
|
// ==============================
|
||
|
|
// Full Campaign Creation Pipeline
|
||
|
|
// ==============================
|
||
|
|
|
||
|
|
export interface FullCampaignParams {
|
||
|
|
campaign_name: string;
|
||
|
|
objective: string;
|
||
|
|
daily_budget: number;
|
||
|
|
targeting: Record<string, unknown>;
|
||
|
|
page_id: string;
|
||
|
|
instagram_actor_id?: string;
|
||
|
|
image_urls?: string[];
|
||
|
|
image_bytes?: string[];
|
||
|
|
link: string;
|
||
|
|
headline: string;
|
||
|
|
primary_text: string;
|
||
|
|
description: string;
|
||
|
|
cta: string;
|
||
|
|
ad_account_id?: string;
|
||
|
|
|
||
|
|
bid_strategy?: string;
|
||
|
|
bid_amount?: number;
|
||
|
|
|
||
|
|
optimization_goal?: string;
|
||
|
|
billing_event?: string;
|
||
|
|
destination_type?: string;
|
||
|
|
promoted_object?: Record<string, unknown>;
|
||
|
|
|
||
|
|
is_whatsapp?: boolean;
|
||
|
|
whatsapp_link?: string;
|
||
|
|
whatsapp_phone_number?: string;
|
||
|
|
}
|
||
|
|
|
||
|
|
export async function createFullCampaign(client: Client, params: FullCampaignParams) {
|
||
|
|
// Step 1: Campaign
|
||
|
|
const campaign = await createCampaign(client, {
|
||
|
|
name: params.campaign_name,
|
||
|
|
objective: params.objective,
|
||
|
|
ad_account_id: params.ad_account_id,
|
||
|
|
});
|
||
|
|
|
||
|
|
const campaignId = (campaign as any)?.id;
|
||
|
|
|
||
|
|
if (!campaignId) {
|
||
|
|
throw new Error(`[Step 1 - Campaign] Falha: ${JSON.stringify(campaign)}`);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Step 2: Ad Set
|
||
|
|
const destinationType = params.destination_type || (params.is_whatsapp ? 'WHATSAPP' : undefined);
|
||
|
|
|
||
|
|
let promotedObject = params.promoted_object;
|
||
|
|
|
||
|
|
if (!promotedObject && params.is_whatsapp) {
|
||
|
|
promotedObject = {
|
||
|
|
page_id: params.page_id,
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
const optimizationGoal =
|
||
|
|
params.optimization_goal ||
|
||
|
|
getDefaultOptimizationGoal(params.objective, destinationType);
|
||
|
|
|
||
|
|
const billingEvent =
|
||
|
|
params.billing_event ||
|
||
|
|
getDefaultBillingEvent(params.objective);
|
||
|
|
|
||
|
|
const adSet = await createAdSet(client, {
|
||
|
|
name: `${params.campaign_name} — Ad Set`,
|
||
|
|
campaign_id: campaignId,
|
||
|
|
daily_budget: params.daily_budget,
|
||
|
|
targeting: params.targeting,
|
||
|
|
objective: params.objective,
|
||
|
|
optimization_goal: optimizationGoal,
|
||
|
|
billing_event: billingEvent,
|
||
|
|
promoted_object: promotedObject,
|
||
|
|
destination_type: destinationType,
|
||
|
|
bid_strategy: params.bid_strategy,
|
||
|
|
bid_amount: params.bid_amount,
|
||
|
|
ad_account_id: params.ad_account_id,
|
||
|
|
});
|
||
|
|
|
||
|
|
const adSetId = (adSet as any)?.id;
|
||
|
|
|
||
|
|
if (!adSetId) {
|
||
|
|
throw new Error(`[Step 2 - Ad Set] Falha: ${JSON.stringify(adSet)}`);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Step 3: Image Uploads
|
||
|
|
const imageHashes: string[] = [];
|
||
|
|
|
||
|
|
const DATA_URI_RE = /^data:image\/[a-zA-Z0-9.+-]+;base64,(.+)$/;
|
||
|
|
|
||
|
|
const processImageUploads = async (urls?: string[], bytes?: string[]) => {
|
||
|
|
const allBytes = [...(bytes || [])];
|
||
|
|
const realUrls: string[] = [];
|
||
|
|
|
||
|
|
// gpt-image-2 returns base64 data URIs (data:image/png;base64,...) — Meta's
|
||
|
|
// upload_ad_image expects those as raw base64 via image_bytes, not image_url
|
||
|
|
// (sending a data URI as image_url causes "tipo de arquivo não suportado").
|
||
|
|
for (const url of urls || []) {
|
||
|
|
const match = DATA_URI_RE.exec(url);
|
||
|
|
if (match) {
|
||
|
|
allBytes.push(match[1]);
|
||
|
|
} else {
|
||
|
|
realUrls.push(url);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
for (const url of realUrls) {
|
||
|
|
const res = await uploadAdImage(client, url, undefined, params.ad_account_id);
|
||
|
|
if ((res as any)?.hash) imageHashes.push((res as any).hash);
|
||
|
|
}
|
||
|
|
|
||
|
|
for (const b of allBytes) {
|
||
|
|
const res = await uploadAdImage(client, undefined, b, params.ad_account_id);
|
||
|
|
if ((res as any)?.hash) imageHashes.push((res as any).hash);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
await processImageUploads(params.image_urls, params.image_bytes);
|
||
|
|
|
||
|
|
if (imageHashes.length === 0) {
|
||
|
|
throw new Error('[Step 3 - Upload] Nenhuma imagem foi enviada ou processada.');
|
||
|
|
}
|
||
|
|
|
||
|
|
// Step 4 & 5: Create Ad Creative and Ad
|
||
|
|
const creativeName = `${params.campaign_name} — Creative`;
|
||
|
|
|
||
|
|
// Use a single primary image via the standard object_story_spec path —
|
||
|
|
// asset_feed_spec (Asset Customization / Dynamic Creative) requires the ad
|
||
|
|
// set to be created with is_dynamic_creative=true, which we don't set here,
|
||
|
|
// and combining it with degrees_of_freedom_spec triggers Meta error subcode
|
||
|
|
// 2490433 ("unexpected error"). The first generated image (1080x1080) is
|
||
|
|
// the most broadly compatible across placements.
|
||
|
|
const primaryImageHash = imageHashes[0];
|
||
|
|
|
||
|
|
let creative;
|
||
|
|
if (params.is_whatsapp) {
|
||
|
|
creative = await createWhatsappAdCreative(client, {
|
||
|
|
name: creativeName,
|
||
|
|
page_id: params.page_id,
|
||
|
|
instagram_actor_id: params.instagram_actor_id,
|
||
|
|
image_hash: primaryImageHash,
|
||
|
|
message: params.primary_text,
|
||
|
|
headline: params.headline,
|
||
|
|
description: params.description,
|
||
|
|
link: params.link,
|
||
|
|
whatsapp_link: params.whatsapp_link,
|
||
|
|
whatsapp_phone_number: params.whatsapp_phone_number,
|
||
|
|
ad_account_id: params.ad_account_id,
|
||
|
|
});
|
||
|
|
} else {
|
||
|
|
creative = await createAdCreative(client, {
|
||
|
|
name: creativeName,
|
||
|
|
page_id: params.page_id,
|
||
|
|
instagram_actor_id: params.instagram_actor_id,
|
||
|
|
image_hash: primaryImageHash,
|
||
|
|
link: params.link,
|
||
|
|
message: params.primary_text,
|
||
|
|
headline: params.headline,
|
||
|
|
description: params.description,
|
||
|
|
call_to_action_type: params.cta,
|
||
|
|
ad_account_id: params.ad_account_id,
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
const creativeId = (creative as any)?.id;
|
||
|
|
if (!creativeId) {
|
||
|
|
throw new Error(`[Step 4 - Creative] Falha no criativo: ${JSON.stringify(creative)}`);
|
||
|
|
}
|
||
|
|
|
||
|
|
const ad = await createAd(client, {
|
||
|
|
name: `${params.campaign_name} — Ad`,
|
||
|
|
adset_id: adSetId,
|
||
|
|
creative_id: creativeId,
|
||
|
|
ad_account_id: params.ad_account_id,
|
||
|
|
});
|
||
|
|
|
||
|
|
const adId = (ad as any)?.id;
|
||
|
|
if (!adId) {
|
||
|
|
throw new Error(`[Step 5 - Ad] Falha no anúncio: ${JSON.stringify(ad)}`);
|
||
|
|
}
|
||
|
|
|
||
|
|
return {
|
||
|
|
campaign_id: campaignId,
|
||
|
|
adset_id: adSetId,
|
||
|
|
creative_ids: [creativeId],
|
||
|
|
ad_ids: [adId],
|
||
|
|
objective: params.objective,
|
||
|
|
optimization_goal: optimizationGoal,
|
||
|
|
billing_event: billingEvent,
|
||
|
|
bid_strategy: params.bid_strategy,
|
||
|
|
bid_amount: params.bid_amount,
|
||
|
|
is_whatsapp: params.is_whatsapp || false,
|
||
|
|
};
|
||
|
|
}
|