134 lines
4.8 KiB
JavaScript
134 lines
4.8 KiB
JavaScript
const OpenAI = require('openai');
|
|
|
|
const WORKFLOW = process.env.WORKFLOW_URL;
|
|
|
|
function getClient() {
|
|
const apiKey = process.env.OPENAI_API_KEY;
|
|
if (!apiKey) throw new Error('OPENAI_API_KEY not set');
|
|
return new OpenAI({ apiKey });
|
|
}
|
|
|
|
const TRAI_RULES = `TRAI SMS Template Rules:
|
|
1. Maximum 160 characters for a single SMS (or 153 per part for multi-part)
|
|
2. Dynamic variables must use the format {#var#}
|
|
3. Transactional templates must not contain promotional content or URLs
|
|
4. The sender ID (header) must be a 6-character alphabetic string registered with DLT
|
|
5. No special characters except: . , - _ / @ and standard punctuation
|
|
6. Template must clearly relate to the registered event type
|
|
7. Do not include any URLs unless explicitly required by the event
|
|
8. Template must start with context (e.g. "Your order..." not "Hi! Your order...")`;
|
|
|
|
const EVENT_DESCRIPTIONS = {
|
|
placed: 'The customer has successfully placed an order',
|
|
confirmed: 'The order has been confirmed by the seller/warehouse',
|
|
dp_assigned: 'A delivery partner has been assigned to deliver the order',
|
|
pack: 'The order has been packed and is ready for dispatch',
|
|
cancelled: 'The order has been cancelled',
|
|
delivery_done: 'The order has been successfully delivered to the customer',
|
|
};
|
|
|
|
/** Parse scraped website data into structured brand context. */
|
|
async function parseBrandContext(scrapedData) {
|
|
const openai = getClient();
|
|
const { markdown, links } = scrapedData;
|
|
|
|
const prompt = `You are a brand analyst. Analyze the website content below and extract structured brand information.
|
|
|
|
Website content (markdown):
|
|
${markdown.slice(0, 8000)}
|
|
|
|
All links found on the page:
|
|
${JSON.stringify(links.slice(0, 200))}
|
|
|
|
Return ONLY a valid JSON object:
|
|
{
|
|
"brandName": "string — the business/brand name",
|
|
"tone": "one of: friendly, professional, formal, casual, energetic",
|
|
"taglines": ["max 3 key brand taglines or phrases found on the page"],
|
|
"colors": ["brand color hex codes if found, else empty array"],
|
|
"relevantImageUrls": ["3-5 relevant brand image URLs — logos, hero images, product shots only. Exclude: social icons, nav icons, tracking pixels, data URIs, generic stock images. Only absolute URLs."]
|
|
}`;
|
|
|
|
const completion = await openai.chat.completions.create({
|
|
model: 'gpt-4o',
|
|
messages: [{ role: 'user', content: prompt }],
|
|
response_format: { type: 'json_object' },
|
|
temperature: 0.3,
|
|
});
|
|
return JSON.parse(completion.choices[0].message.content);
|
|
}
|
|
|
|
/** Generate 3 TRAI-compliant SMS template variants for a given event. */
|
|
async function generateTemplates(brandContext, eventSlug, eventLabel) {
|
|
const openai = getClient();
|
|
const eventDesc = EVENT_DESCRIPTIONS[eventSlug] || `A "${eventLabel}" event in the order lifecycle`;
|
|
|
|
const prompt = `You are an SMS template expert for e-commerce in India.
|
|
|
|
Brand: ${brandContext.brandName}
|
|
Tone: ${brandContext.tone}
|
|
Taglines: ${(brandContext.taglines || []).join(', ') || 'none'}
|
|
Event: "${eventLabel}" — ${eventDesc}
|
|
|
|
${TRAI_RULES}
|
|
|
|
Generate exactly 3 distinct SMS templates. Each must:
|
|
- Strictly follow all TRAI rules
|
|
- Use {#var#} for every dynamic variable (e.g. customer name, order ID, product name, tracking URL, delivery date)
|
|
- Be under 160 characters
|
|
- Reflect the brand tone
|
|
- Start with order/event context
|
|
|
|
Return ONLY valid JSON:
|
|
{ "templates": ["template 1", "template 2", "template 3"] }`;
|
|
|
|
const completion = await openai.chat.completions.create({
|
|
model: 'gpt-4o',
|
|
messages: [{ role: 'user', content: prompt }],
|
|
response_format: { type: 'json_object' },
|
|
temperature: 0.7,
|
|
});
|
|
const result = JSON.parse(completion.choices[0].message.content);
|
|
return result.templates || [];
|
|
}
|
|
|
|
/** Parse a raw provider cURL and map its placeholders to the approved template's {#var#} positions. */
|
|
async function processCurl(rawCurl, approvedTemplate, eventSlug) {
|
|
const openai = getClient();
|
|
|
|
const prompt = `You are an SMS provider integration expert.
|
|
|
|
Approved SMS template:
|
|
${approvedTemplate}
|
|
|
|
Event: ${eventSlug}
|
|
|
|
Raw cURL from SMS provider:
|
|
${rawCurl}
|
|
|
|
Analyze the cURL:
|
|
1. Identify all placeholder formats (e.g. {{variable}}, %VAR%, <PLACEHOLDER>)
|
|
2. Map each to a semantic field name (e.g. customerName, orderId, productName, trackingUrl, deliveryDate)
|
|
3. Normalize to camelCase in processedCurl
|
|
4. Build variableMap matching positional {#var#} slots in the approved template
|
|
|
|
Return ONLY valid JSON:
|
|
{
|
|
"processedCurl": "cURL with normalized placeholder names",
|
|
"variableMap": {
|
|
"{#var#}[0]": "fieldName for first {#var#}",
|
|
"{#var#}[1]": "fieldName for second {#var#}"
|
|
}
|
|
}`;
|
|
|
|
const completion = await openai.chat.completions.create({
|
|
model: 'gpt-4o',
|
|
messages: [{ role: 'user', content: prompt }],
|
|
response_format: { type: 'json_object' },
|
|
temperature: 0.2,
|
|
});
|
|
return JSON.parse(completion.choices[0].message.content);
|
|
}
|
|
|
|
module.exports = { parseBrandContext, generateTemplates, processCurl };
|