diff --git a/src/modules/whatsapp/repositories/whatsapp-template.repository.ts b/src/modules/whatsapp/repositories/whatsapp-template.repository.ts index fdb9f03..ba4481e 100644 --- a/src/modules/whatsapp/repositories/whatsapp-template.repository.ts +++ b/src/modules/whatsapp/repositories/whatsapp-template.repository.ts @@ -163,6 +163,52 @@ export class WhatsappTemplateRepository { return result.rowCount || 0; } + async importFromMeta(input: { + metaTemplateId: string; + metaTemplateName: string; + metaStatus: string; + language: string; + category: string; + content: string; + headerType?: string | null; + headerText?: string | null; + footerText?: string | null; + buttonsJson: string | null; + }): Promise { + try { + const result = await this.database.query( + `INSERT INTO whatsapp_templates ( + name, content, category, language, + meta_template_name, meta_template_id, meta_status, + header_type, header_text, footer_text, buttons, body_variables, + status, meta_approved_at, updated_at + ) + VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11::jsonb, NULL, + CASE WHEN $7 = 'APPROVED' THEN 'approved' ELSE 'pending' END, + CASE WHEN $7 = 'APPROVED' THEN CURRENT_TIMESTAMP ELSE NULL END, + CURRENT_TIMESTAMP + ) + ON CONFLICT (meta_template_name) WHERE meta_template_name IS NOT NULL DO NOTHING`, + [ + input.metaTemplateName, + input.content, + input.category, + input.language, + input.metaTemplateName, + input.metaTemplateId, + input.metaStatus, + input.headerType || null, + input.headerText || null, + input.footerText || null, + input.buttonsJson, + ], + ); + return (result.rowCount || 0) > 0; + } catch { + return false; + } + } + deleteTemplate(id: number) { return this.database.query('DELETE FROM whatsapp_templates WHERE id = $1 RETURNING meta_template_name', [id]); } diff --git a/src/modules/whatsapp/whatsapp-template.service.ts b/src/modules/whatsapp/whatsapp-template.service.ts index b8b3e38..09b8045 100644 --- a/src/modules/whatsapp/whatsapp-template.service.ts +++ b/src/modules/whatsapp/whatsapp-template.service.ts @@ -114,12 +114,12 @@ export class WhatsappTemplateService { async syncFromMeta() { const config = await this.configService.getConfig(); if (!config.access_token || !config.waba_id) { - return { synced: 0, error: 'WhatsApp não configurado com WABA ID' }; + return { synced: 0, imported: 0, error: 'WhatsApp não configurado com WABA ID' }; } try { const response = await fetch( - `https://graph.facebook.com/${config.api_version}/${config.waba_id}/message_templates?fields=id,name,status&limit=250`, + `https://graph.facebook.com/${config.api_version}/${config.waba_id}/message_templates?fields=id,name,status,language,category,components&limit=250`, { headers: { Authorization: `Bearer ${config.access_token}` } }, ); @@ -127,19 +127,44 @@ export class WhatsappTemplateService { if (!response.ok || !Array.isArray(data?.data)) { this.logger.error('[Meta Templates] Erro ao buscar lista:', data?.error); - return { synced: 0, error: data?.error?.message || 'Erro ao sincronizar' }; + return { synced: 0, imported: 0, error: data?.error?.message || 'Erro ao sincronizar' }; } let synced = 0; + let imported = 0; + for (const t of data.data) { - const updated = await this.repo.updateMetaStatusByName(t.name, t.status, String(t.id)); - if (updated > 0) synced++; + const metaId = String(t.id); + const updated = await this.repo.updateMetaStatusByName(t.name, t.status, metaId); + if (updated > 0) { + synced++; + continue; + } + + const parsed = this.parseComponents(t.components || []); + const wasInserted = await this.repo.importFromMeta({ + metaTemplateId: metaId, + metaTemplateName: t.name, + metaStatus: t.status, + language: t.language || 'pt_BR', + category: this.normalizeCategory(t.category), + content: parsed.content, + headerType: parsed.headerType, + headerText: parsed.headerText, + footerText: parsed.footerText, + buttonsJson: parsed.buttons?.length ? JSON.stringify(parsed.buttons) : null, + }); + + if (wasInserted) { + imported++; + this.logger.log(`[Meta Templates] Importado: "${t.name}" (${t.status})`); + } } - return { synced }; + return { synced, imported }; } catch (err: any) { this.logger.error('[Meta Templates] Falha na sincronização:', err?.message); - return { synced: 0, error: 'Erro de conexão com a Meta' }; + return { synced: 0, imported: 0, error: 'Erro de conexão com a Meta' }; } } @@ -223,6 +248,33 @@ export class WhatsappTemplateService { return components; } + private parseComponents(components: any[]): { + content: string; + headerType?: string | null; + headerText?: string | null; + footerText?: string | null; + buttons?: any[]; + } { + const result: any = { content: '', headerType: null, headerText: null, footerText: null, buttons: [] }; + for (const c of components || []) { + if (c.type === 'BODY') result.content = c.text || ''; + else if (c.type === 'HEADER') { + result.headerType = c.format || null; + result.headerText = c.format === 'TEXT' ? (c.text || null) : null; + } + else if (c.type === 'FOOTER') result.footerText = c.text || null; + else if (c.type === 'BUTTONS') { + result.buttons = (c.buttons || []).map((b: any) => ({ + type: b.type, + text: b.text || '', + url: b.url || '', + phone: b.phone_number || '', + })); + } + } + return result; + } + private toMetaName(displayName: string): string { return String(displayName || '') .normalize('NFD')