Mapea EAHYK-l1SFM, EAHYK9p_MlY y EAHYK_ylNJo por formato y plataforma en RRSS. Co-authored-by: Cursor <cursoragent@cursor.com>
217 lines
6.5 KiB
TypeScript
217 lines
6.5 KiB
TypeScript
/**
|
|
* RRSS auto-generation: reuse Ads generation_requests → n8n (Claude + Freepik + Canva)
|
|
* with channel=rrss and write-back into creative_pipeline.
|
|
*/
|
|
|
|
export type RrssPipelineFormat = 'post' | 'story' | 'reel' | 'carrusel';
|
|
|
|
export type RrssPlacement = {
|
|
aspect_ratio: string;
|
|
role: string;
|
|
size: string;
|
|
meta_key: string;
|
|
canva_template_key?: string;
|
|
};
|
|
|
|
/** Placements per RRSS format (not the Ads triple 4:5+9:16+1.91:1). */
|
|
export function rrssBriefAspectFields(
|
|
format: RrssPipelineFormat,
|
|
platform?: string | null
|
|
): {
|
|
aspect_ratio: string;
|
|
aspect_ratios: string[];
|
|
required_placements: RrssPlacement[];
|
|
} {
|
|
const isLinkedIn = String(platform || '')
|
|
.toLowerCase()
|
|
.includes('linkedin');
|
|
|
|
if (format === 'story') {
|
|
const p: RrssPlacement = {
|
|
aspect_ratio: '9:16',
|
|
role: 'stories',
|
|
size: '1080x1920',
|
|
meta_key: 'asset_url',
|
|
canva_template_key: 'stories_9_16',
|
|
};
|
|
return { aspect_ratio: '9:16', aspect_ratios: ['9:16'], required_placements: [p] };
|
|
}
|
|
if (format === 'reel') {
|
|
const p: RrssPlacement = {
|
|
aspect_ratio: '9:16',
|
|
role: 'reel',
|
|
size: '1080x1920',
|
|
meta_key: 'asset_url',
|
|
canva_template_key: 'stories_9_16',
|
|
};
|
|
return { aspect_ratio: '9:16', aspect_ratios: ['9:16'], required_placements: [p] };
|
|
}
|
|
if (isLinkedIn) {
|
|
const p: RrssPlacement = {
|
|
aspect_ratio: '1:1',
|
|
role: 'linkedin',
|
|
size: '1080x1080',
|
|
meta_key: 'asset_url',
|
|
canva_template_key: 'linkedin',
|
|
};
|
|
return { aspect_ratio: '1:1', aspect_ratios: ['1:1'], required_placements: [p] };
|
|
}
|
|
if (format === 'carrusel') {
|
|
const p: RrssPlacement = {
|
|
aspect_ratio: '4:5',
|
|
role: 'carousel_slide',
|
|
size: '1080x1350',
|
|
meta_key: 'asset_url',
|
|
canva_template_key: 'post',
|
|
};
|
|
return {
|
|
aspect_ratio: '4:5',
|
|
aspect_ratios: ['4:5'],
|
|
required_placements: [p],
|
|
};
|
|
}
|
|
// post feed (IG/FB)
|
|
const p: RrssPlacement = {
|
|
aspect_ratio: '4:5',
|
|
role: 'feed',
|
|
size: '1080x1350',
|
|
meta_key: 'asset_url',
|
|
canva_template_key: 'post',
|
|
};
|
|
return { aspect_ratio: '4:5', aspect_ratios: ['4:5'], required_placements: [p] };
|
|
}
|
|
|
|
export function isRrssPipelineFormat(f: unknown): f is RrssPipelineFormat {
|
|
return f === 'post' || f === 'story' || f === 'reel' || f === 'carrusel';
|
|
}
|
|
|
|
export function mapRrssFormatToSkillFormat(format: RrssPipelineFormat): string {
|
|
return format; // skills/formats/{post,story,reel,carrusel}.md
|
|
}
|
|
|
|
export type RrssCalendarPieceBrief = {
|
|
id?: string | null;
|
|
title?: string | null;
|
|
date?: string | null;
|
|
time?: string | null;
|
|
format?: string | null;
|
|
pillar?: string | null;
|
|
hook?: string | null;
|
|
body?: string | null;
|
|
cta?: string | null;
|
|
hashtags?: string | null;
|
|
platform?: string | null;
|
|
};
|
|
|
|
export type RrssGenerationBriefInput = {
|
|
tenantId: string;
|
|
clientSlug: string;
|
|
clientName: string;
|
|
format: RrssPipelineFormat;
|
|
pipelineId: string;
|
|
calendarPieceId?: string | null;
|
|
title: string;
|
|
caption?: string | null;
|
|
notes?: string | null;
|
|
pillar?: string | null;
|
|
month?: string | null;
|
|
scheduledAt?: string | null;
|
|
platform?: string | null;
|
|
calendarPiece?: RrssCalendarPieceBrief | null;
|
|
/** Recent pipeline captions/titles for style continuity */
|
|
recentPipeline?: Array<{ title: string; format: string; caption?: string | null }>;
|
|
/** Brandbook prompt snippet (already compiled or raw fields) */
|
|
brandbookPrompt?: string | null;
|
|
/** Recent organic posts snapshot */
|
|
socialHistory?: unknown;
|
|
variantsRequested?: number;
|
|
extraNotes?: string | null;
|
|
/** Resolved Canva Brand Template IDs for RRSS (nested ads/rrss or flat). */
|
|
canvaTemplates?: Record<string, string> | null;
|
|
};
|
|
|
|
export function buildRrssGenerationBrief(input: RrssGenerationBriefInput) {
|
|
const cal = input.calendarPiece || {};
|
|
const platform =
|
|
input.platform ||
|
|
(typeof cal.platform === 'string' ? cal.platform : null) ||
|
|
null;
|
|
const aspects = rrssBriefAspectFields(input.format, platform);
|
|
return {
|
|
channel: 'rrss' as const,
|
|
tenant_id: input.tenantId,
|
|
client_id: input.tenantId,
|
|
client_slug: input.clientSlug,
|
|
client_name: input.clientName,
|
|
canva_templates: input.canvaTemplates || {},
|
|
reason: 'calendario' as const,
|
|
source_creative_id: null,
|
|
source_pipeline_id: input.pipelineId,
|
|
calendar_piece_id: input.calendarPieceId || cal.id || null,
|
|
format: input.format,
|
|
platform,
|
|
skill_format: mapRrssFormatToSkillFormat(input.format),
|
|
...aspects,
|
|
variants_requested: Number(input.variantsRequested || 2),
|
|
language: 'es',
|
|
title: input.title,
|
|
caption: input.caption || null,
|
|
pillar: input.pillar || cal.pillar || null,
|
|
month: input.month || null,
|
|
scheduled_at: input.scheduledAt || null,
|
|
calendar_piece: {
|
|
id: input.calendarPieceId || cal.id || null,
|
|
title: cal.title || input.title,
|
|
date: cal.date || null,
|
|
time: cal.time || null,
|
|
format: cal.format || input.format,
|
|
pillar: cal.pillar || input.pillar || null,
|
|
hook: cal.hook || null,
|
|
body: cal.body || input.caption || null,
|
|
cta: cal.cta || null,
|
|
hashtags: cal.hashtags || null,
|
|
platform: cal.platform || 'instagram',
|
|
},
|
|
context: {
|
|
brandbook_prompt: input.brandbookPrompt || null,
|
|
recent_pipeline: (input.recentPipeline || []).slice(0, 12),
|
|
social_history: input.socialHistory || null,
|
|
},
|
|
notes:
|
|
[
|
|
input.extraNotes,
|
|
input.notes,
|
|
'Canal RRSS · producir pieza para creative_pipeline (no Meta Ads).',
|
|
'Escribir resultado vía POST /api/integrations/creative-pipeline con generation_request_id.',
|
|
]
|
|
.filter(Boolean)
|
|
.join('\n'),
|
|
write_back: {
|
|
target: 'creative_pipeline',
|
|
pipeline_id: input.pipelineId,
|
|
endpoint: '/api/integrations/creative-pipeline',
|
|
},
|
|
};
|
|
}
|
|
|
|
/** Marker stored in pipeline.notes while auto-generation is pending. */
|
|
export function generationRequestNoteMarker(grId: string): string {
|
|
return `[[generation_request:${grId}]]`;
|
|
}
|
|
|
|
export function parseGenerationRequestIdFromNotes(
|
|
notes: string | null | undefined
|
|
): string | null {
|
|
if (!notes) return null;
|
|
const m = notes.match(/\[\[generation_request:([0-9a-f-]{36})\]\]/i);
|
|
return m?.[1] || null;
|
|
}
|
|
|
|
export function stripGenerationRequestMarker(notes: string | null | undefined): string {
|
|
if (!notes) return '';
|
|
return notes
|
|
.replace(/\[\[generation_request:[0-9a-f-]{36}\]\]/gi, '')
|
|
.replace(/\n{3,}/g, '\n\n')
|
|
.trim();
|
|
}
|