feat: add base components
This commit is contained in:
@@ -0,0 +1,153 @@
|
||||
import { TemplateTypeDetector } from './TemplateTypeDetector';
|
||||
|
||||
/**
|
||||
* TemplateNormalizer - Convert platform-specific formats to unified structure
|
||||
*/
|
||||
export class TemplateNormalizer {
|
||||
/**
|
||||
* Normalize WhatsApp template to unified format
|
||||
* @param {Object} template - WhatsApp template object
|
||||
* @returns {Object} - Normalized template
|
||||
*/
|
||||
static normalizeWhatsApp(template) {
|
||||
const components = template.components || [];
|
||||
|
||||
return {
|
||||
id: template.id,
|
||||
name: template.name,
|
||||
platform: 'whatsapp',
|
||||
type: TemplateTypeDetector.detectWhatsAppType(template),
|
||||
parameterFormat: template.parameter_format || 'POSITIONAL',
|
||||
header: components.find(c => c.type === 'HEADER'),
|
||||
body: components.find(c => c.type === 'BODY'),
|
||||
footer: components.find(c => c.type === 'FOOTER'),
|
||||
buttons: components.find(c => c.type === 'BUTTONS')?.buttons || [],
|
||||
variables: this.extractWhatsAppVariables(template),
|
||||
category: template.category,
|
||||
language: template.language,
|
||||
originalTemplate: template,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize Twilio template to unified format
|
||||
* @param {Object} template - Twilio template object
|
||||
* @returns {Object} - Normalized template
|
||||
*/
|
||||
static normalizeTwilio(template) {
|
||||
const typeData = template.types?.[`twilio/${template.template_type}`] || {};
|
||||
|
||||
return {
|
||||
contentSid: template.content_sid,
|
||||
name: template.friendly_name,
|
||||
platform: 'twilio',
|
||||
type: TemplateTypeDetector.detectTwilioType(template),
|
||||
body: template.body || typeData.body,
|
||||
media: typeData.media || [],
|
||||
actions: typeData.actions || [],
|
||||
variables: template.variables || {},
|
||||
category: template.category || 'utility',
|
||||
language: template.language || 'en',
|
||||
originalTemplate: template,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract variables from WhatsApp template
|
||||
* @param {Object} template - WhatsApp template object
|
||||
* @returns {Object} - Variables object with example values
|
||||
*/
|
||||
static extractWhatsAppVariables(template) {
|
||||
const variables = {};
|
||||
const components = template.components || [];
|
||||
|
||||
components.forEach(component => {
|
||||
// Extract from text content
|
||||
if (component.text) {
|
||||
const matches = component.text.match(/\{\{([^}]+)\}\}/g) || [];
|
||||
matches.forEach(match => {
|
||||
const variable = match.replace(/[{}]/g, '');
|
||||
|
||||
if (template.parameter_format === 'NAMED') {
|
||||
// Named parameters: {{customer_name}}
|
||||
const example =
|
||||
component.example?.body_text_named_params?.find(
|
||||
p => p.param_name === variable
|
||||
)?.example || '';
|
||||
variables[variable] = example;
|
||||
} else {
|
||||
// Positional parameters: {{1}}, {{2}}
|
||||
const position = parseInt(variable, 10) - 1;
|
||||
const example = component.example?.body_text?.[0]?.[position] || '';
|
||||
variables[variable] = example;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Extract from button URLs
|
||||
if (component.buttons) {
|
||||
component.buttons.forEach(button => {
|
||||
if (button.url) {
|
||||
const matches = button.url.match(/\{\{([^}]+)\}\}/g) || [];
|
||||
matches.forEach(match => {
|
||||
const variable = match.replace(/[{}]/g, '');
|
||||
const example = button.example?.[0] || '';
|
||||
variables[variable] = example;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return variables;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract variables from Twilio template
|
||||
* @param {Object} template - Twilio template object
|
||||
* @returns {Object} - Variables object with example values
|
||||
*/
|
||||
static extractTwilioVariables(template) {
|
||||
const variables = {};
|
||||
|
||||
// Extract from body text
|
||||
if (template.body) {
|
||||
const matches = template.body.match(/\{\{([^}]+)\}\}/g) || [];
|
||||
matches.forEach(match => {
|
||||
const variable = match.replace(/[{}]/g, '');
|
||||
variables[variable] = template.variables?.[variable] || '';
|
||||
});
|
||||
}
|
||||
|
||||
// Extract from media URLs
|
||||
const typeData = template.types?.[`twilio/${template.template_type}`] || {};
|
||||
if (typeData.media) {
|
||||
typeData.media.forEach(mediaUrl => {
|
||||
const matches = mediaUrl.match(/\{\{([^}]+)\}\}/g) || [];
|
||||
matches.forEach(match => {
|
||||
const variable = match.replace(/[{}]/g, '');
|
||||
variables[variable] = template.variables?.[variable] || '';
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return variables;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize template from any platform
|
||||
* @param {Object} template - Template object
|
||||
* @param {string} platform - Platform identifier ('whatsapp' | 'twilio')
|
||||
* @returns {Object} - Normalized template
|
||||
*/
|
||||
static normalize(template, platform) {
|
||||
switch (platform) {
|
||||
case 'whatsapp':
|
||||
return this.normalizeWhatsApp(template);
|
||||
case 'twilio':
|
||||
return this.normalizeTwilio(template);
|
||||
default:
|
||||
throw new Error(`Unsupported platform: ${platform}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/**
|
||||
* TemplateTypeDetector - Unified service to identify template types across platforms
|
||||
*/
|
||||
export class TemplateTypeDetector {
|
||||
/**
|
||||
* Detect WhatsApp template type based on components
|
||||
* @param {Object} template - WhatsApp template object
|
||||
* @returns {string} - Template type identifier
|
||||
*/
|
||||
static detectWhatsAppType(template) {
|
||||
const components = template.components || [];
|
||||
const hasHeader = components.find(c => c.type === 'HEADER');
|
||||
const hasButtons = components.find(c => c.type === 'BUTTONS');
|
||||
|
||||
// Media templates (priority: header media)
|
||||
if (hasHeader?.format === 'IMAGE') return 'whatsapp-media-image';
|
||||
if (hasHeader?.format === 'VIDEO') return 'whatsapp-media-video';
|
||||
if (hasHeader?.format === 'DOCUMENT') return 'whatsapp-media-document';
|
||||
|
||||
// Interactive templates (priority: special buttons)
|
||||
if (hasButtons) {
|
||||
const copyCodeButton = hasButtons.buttons?.find(
|
||||
b => b.type === 'COPY_CODE'
|
||||
);
|
||||
if (copyCodeButton) return 'whatsapp-copy-code';
|
||||
return 'whatsapp-interactive';
|
||||
}
|
||||
|
||||
// Text templates
|
||||
if (hasHeader?.format === 'TEXT') return 'whatsapp-text-header';
|
||||
return 'whatsapp-text';
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect Twilio template type
|
||||
* @param {Object} template - Twilio template object
|
||||
* @returns {string} - Template type identifier
|
||||
*/
|
||||
static detectTwilioType(template) {
|
||||
switch (template.template_type) {
|
||||
case 'media':
|
||||
return 'twilio-media';
|
||||
case 'quick_reply':
|
||||
return 'twilio-quick-reply';
|
||||
default:
|
||||
return 'twilio-text';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all supported template types
|
||||
* @returns {Array} - Array of supported template types
|
||||
*/
|
||||
static getSupportedTypes() {
|
||||
return [
|
||||
// WhatsApp types
|
||||
'whatsapp-text',
|
||||
'whatsapp-text-header',
|
||||
'whatsapp-media-image',
|
||||
'whatsapp-media-video',
|
||||
'whatsapp-media-document',
|
||||
'whatsapp-interactive',
|
||||
'whatsapp-copy-code',
|
||||
|
||||
// Twilio types
|
||||
'twilio-text',
|
||||
'twilio-media',
|
||||
'twilio-quick-reply',
|
||||
];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user