html-ad-generator-mcp
Version:
MCP server for generating HTML ad templates from JSON input for Google Ads, Meta Ads, and Moment Science
126 lines • 8.01 kB
JavaScript
import { readFileSync } from 'fs';
import { join, dirname } from 'path';
import { fileURLToPath } from 'url';
const __dirname = dirname(fileURLToPath(import.meta.url));
export function processGoogleTemplate(input) {
// Read the base template
const templatePath = join(__dirname, '../../templates/google-base.html');
let html = readFileSync(templatePath, 'utf-8');
// ---- Populate search ad dropdowns dynamically ----
if (input.searchAd) {
function insertOptions(selectRegex, options, fieldId) {
const match = html.match(selectRegex);
if (match) {
const existing = match[1];
let newOptions = '';
options.forEach(opt => {
if (opt && !existing.includes(opt)) {
newOptions += `\n <div class="dropdown-option" onclick="selectOption('${fieldId}', this)">${escapeHtml(opt)}</div>`;
}
});
html = html.replace(selectRegex, `<div class="dropdown-options" id="${fieldId.split('-')[0]}-options">${newOptions}${existing}</div>`);
}
}
// Headlines
insertOptions(/<div class="dropdown-options" id="search-headline-options">([\s\S]*?)<\/div>/, input.searchAd.headlines, 'search-headline-text');
// Descriptions
insertOptions(/<div class="dropdown-options" id="search-description-options">([\s\S]*?)<\/div>/, input.searchAd.descriptions, 'search-description-text');
// Update preview
const headlinePreview = input.searchAd.headlines.join(' - ');
html = html.replace(/<div class="search-ad-headline" id="search-preview-headline">.*?<\/div>/, `<div class="search-ad-headline" id="search-preview-headline">${escapeHtml(headlinePreview)}</div>`);
if (input.searchAd.descriptions[0]) {
html = html.replace(/<div class="search-ad-description" id="search-preview-description-1">.*?<\/div>/, `<div class="search-ad-description" id="search-preview-description-1">${escapeHtml(input.searchAd.descriptions[0])}</div>`);
}
if (input.searchAd.descriptions[1]) {
html = html.replace(/<div class="search-ad-description" id="search-preview-description-2">.*?<\/div>/, `<div class="search-ad-description" id="search-preview-description-2">${escapeHtml(input.searchAd.descriptions[1])}</div>`);
}
}
// ---- Populate display ad dropdowns dynamically ----
if (input.displayAd) {
function insertDisplayOptions(selectRegex, options, fieldId) {
const match = html.match(selectRegex);
if (match) {
const existing = match[1];
let newOptions = '';
options.forEach(opt => {
if (opt && !existing.includes(opt)) {
newOptions += `\n <div class="dropdown-option" onclick="selectOption('${fieldId}', this)">${escapeHtml(opt)}</div>`;
}
});
html = html.replace(selectRegex, `<div class="dropdown-options" id="${fieldId.split('-')[0]}-options">${newOptions}${existing}</div>`);
}
}
function insertDisplayOption(selectRegex, option, fieldId) {
const match = html.match(selectRegex);
if (match && !match[1].includes(option)) {
const newOption = `\n <div class="dropdown-option" onclick="selectOption('${fieldId}', this)">${escapeHtml(option)}</div>`;
html = html.replace(selectRegex, `<div class="dropdown-options" id="${fieldId.split('-')[0]}-options">${newOption}${match[1]}</div>`);
}
}
// Use array handling for content fields
insertDisplayOptions(/<div class="dropdown-options" id="display-headline-options">([\s\S]*?)<\/div>/, input.displayAd.headline, 'display-headline-text');
insertDisplayOptions(/<div class="dropdown-options" id="display-long-headline-options">([\s\S]*?)<\/div>/, input.displayAd.longHeadline, 'display-long-headline-text');
insertDisplayOptions(/<div class="dropdown-options" id="display-description-options">([\s\S]*?)<\/div>/, input.displayAd.description, 'display-description-text');
// Business name is still single value
insertDisplayOption(/<div class="dropdown-options" id="display-business-options">([\s\S]*?)<\/div>/, input.displayAd.businessName, 'display-business-text');
// Update preview fields - use first element from arrays
html = html.replace(/<div class="display-ad-headline" id="display-preview-headline">.*?<\/div>/, `<div class="display-ad-headline" id="display-preview-headline">${escapeHtml(input.displayAd.headline[0] || '')}</div>`);
html = html.replace(/<div class="display-ad-headline" id="display-preview-long-headline">.*?<\/div>/, `<div class="display-ad-headline" id="display-preview-long-headline">${escapeHtml(input.displayAd.longHeadline[0] || '')}</div>`);
html = html.replace(/<div class="display-ad-description" id="display-preview-description">.*?<\/div>/, `<div class="display-ad-description" id="display-preview-description">${escapeHtml(input.displayAd.description[0] || '')}</div>`);
html = html.replace(/<div class="display-ad-business" id="display-preview-business">.*?<\/div>/, `<div class="display-ad-business" id="display-preview-business">${escapeHtml(input.displayAd.businessName)}</div>`);
// Update image if provided
if (input.displayAd.imageUrl) {
html = html.replace(/src="https:\/\/picsum\.photos\/seed\/fashion123\/600\/600\.jpg"/, `src="${escapeHtml(input.displayAd.imageUrl)}"`);
}
}
// Update character counts in JavaScript
html = updateCharacterCounts(html, input);
return html;
}
function getDefaultHeadline(index) {
const defaults = ['Premium Running Shoes', 'Lightweight & Comfortable', 'Shop Now - 50% Off'];
return defaults[index] || '';
}
function getDefaultDescription(index) {
const defaults = [
'Experience ultimate comfort with our premium running shoes. Perfect for all terrains.',
'Limited time offer: Buy 1 get 1 free. Free shipping on orders over $50.'
];
return defaults[index] || '';
}
function escapeHtml(text) {
const map = {
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": '''
};
return text.replace(/[&<>"']/g, m => map[m]);
}
function escapeRegex(text) {
return text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
function updateCharacterCounts(html, input) {
if (input.searchAd) {
input.searchAd.headlines.forEach((headline, index) => {
const countId = `search-headline-${index + 1}-count`;
const regex = new RegExp(`id="${countId}">\\d+/30</div>`);
html = html.replace(regex, `id="${countId}">${headline.length}/30</div>`);
});
input.searchAd.descriptions.forEach((desc, index) => {
const countId = `search-description-${index + 1}-count`;
const regex = new RegExp(`id="${countId}">\\d+/90</div>`);
html = html.replace(regex, `id="${countId}">${desc.length}/90</div>`);
});
}
if (input.displayAd) {
// Use first element from arrays for character counts
html = html.replace(/id="display-headline-count">\d+\/30</, `id="display-headline-count">${(input.displayAd.headline[0] || '').length}/30<`);
html = html.replace(/id="display-long-headline-count">\d+\/90</, `id="display-long-headline-count">${(input.displayAd.longHeadline[0] || '').length}/90<`);
html = html.replace(/id="display-description-count">\d+\/90</, `id="display-description-count">${(input.displayAd.description[0] || '').length}/90<`);
html = html.replace(/id="display-business-count">\d+\/25</, `id="display-business-count">${input.displayAd.businessName.length}/25<`);
}
return html;
}
//# sourceMappingURL=google.js.map