144 lines
4.8 KiB
JavaScript
144 lines
4.8 KiB
JavaScript
const WhatsAppTemplate = require('../models/WhatsAppTemplate');
|
|
const { getOrgWhatsAppCredentials, listMessageTemplates } = require('../utils/metaWhatsApp');
|
|
|
|
function normLang(s) {
|
|
return String(s || '')
|
|
.trim()
|
|
.replace(/-/g, '_')
|
|
.toLowerCase();
|
|
}
|
|
|
|
function rejectionFromMeta(m) {
|
|
if (!m || typeof m !== 'object') return '';
|
|
return String(m.rejected_reason || m.reason || '').trim();
|
|
}
|
|
|
|
function normalizeCategory(raw) {
|
|
const c = String(raw || '')
|
|
.trim()
|
|
.toUpperCase();
|
|
if (c === 'MARKETING' || c === 'UTILITY' || c === 'AUTHENTICATION') return c;
|
|
return 'UTILITY';
|
|
}
|
|
|
|
async function resolveCreatedBy(organizationId, locals) {
|
|
for (const loc of locals || []) {
|
|
if (loc.createdBy) return loc.createdBy;
|
|
}
|
|
try {
|
|
const User = require('../models/User');
|
|
const u = await User.findOne({ organization: organizationId }).select('_id').lean();
|
|
if (u?._id) return u._id;
|
|
} catch {
|
|
/* ignore */
|
|
}
|
|
return undefined;
|
|
}
|
|
|
|
/**
|
|
* Pull template rows from Meta and upsert local docs (by metaTemplateId or name+language).
|
|
* Creates local rows for Meta templates that are not yet in the dashboard.
|
|
* @param {import('mongoose').Types.ObjectId|string} organizationId
|
|
* @param {{ onlyNames?: string[] }} [opts] — when set, only upsert these Meta template names
|
|
*/
|
|
async function syncOrganizationTemplates(organizationId, opts = {}) {
|
|
const creds = await getOrgWhatsAppCredentials(organizationId);
|
|
if (!creds) {
|
|
return { ok: false, error: 'WhatsApp Cloud is not configured for this organisation (WABA + access token).' };
|
|
}
|
|
|
|
const data = await listMessageTemplates(creds.wabaId, creds.accessToken);
|
|
let list = Array.isArray(data.data) ? data.data : [];
|
|
const onlyNames = Array.isArray(opts.onlyNames)
|
|
? opts.onlyNames.map((n) => String(n || '').trim().toLowerCase()).filter(Boolean)
|
|
: null;
|
|
if (onlyNames && onlyNames.length) {
|
|
list = list.filter((m) => onlyNames.includes(String(m.name || '').trim().toLowerCase()));
|
|
}
|
|
|
|
const locals = await WhatsAppTemplate.find({ organization: organizationId }).lean();
|
|
const now = new Date();
|
|
let updated = 0;
|
|
let created = 0;
|
|
|
|
for (const loc of locals) {
|
|
let m = null;
|
|
if (loc.metaTemplateId) {
|
|
m = list.find((x) => String(x.id) === String(loc.metaTemplateId));
|
|
}
|
|
if (!m) {
|
|
m = list.find((x) => x.name === loc.name && normLang(x.language) === normLang(loc.language));
|
|
}
|
|
if (m) {
|
|
const st = m.status != null ? String(m.status).toUpperCase() : loc.status;
|
|
const $set = {
|
|
metaTemplateId: String(m.id),
|
|
status: st,
|
|
rejectionReason: rejectionFromMeta(m),
|
|
lastSyncedAt: now,
|
|
lastError: '',
|
|
};
|
|
if (m.category) $set.category = normalizeCategory(m.category);
|
|
if (Array.isArray(m.components) && m.components.length) $set.components = m.components;
|
|
await WhatsAppTemplate.updateOne({ _id: loc._id }, { $set });
|
|
updated += 1;
|
|
}
|
|
}
|
|
|
|
const createdBy = await resolveCreatedBy(organizationId, locals);
|
|
|
|
for (const m of list) {
|
|
const name = String(m.name || '').trim();
|
|
const language = String(m.language || '').trim().replace(/-/g, '_');
|
|
if (!name || !language) continue;
|
|
const exists = locals.some(
|
|
(loc) =>
|
|
(loc.metaTemplateId && String(loc.metaTemplateId) === String(m.id)) ||
|
|
(loc.name === name && normLang(loc.language) === normLang(language)),
|
|
);
|
|
if (exists) continue;
|
|
|
|
try {
|
|
await WhatsAppTemplate.create({
|
|
organization: organizationId,
|
|
name,
|
|
language,
|
|
category: normalizeCategory(m.category),
|
|
components: Array.isArray(m.components) ? m.components : [],
|
|
metaTemplateId: m.id != null ? String(m.id) : '',
|
|
status: m.status != null ? String(m.status).toUpperCase() : 'PENDING',
|
|
rejectionReason: rejectionFromMeta(m),
|
|
lastSyncedAt: now,
|
|
lastError: '',
|
|
variableBindings: { header: [], body: [], url: [] },
|
|
...(createdBy ? { createdBy } : {}),
|
|
});
|
|
created += 1;
|
|
} catch (e) {
|
|
if (e && e.code === 11000) {
|
|
await WhatsAppTemplate.updateOne(
|
|
{ organization: organizationId, name, language },
|
|
{
|
|
$set: {
|
|
metaTemplateId: m.id != null ? String(m.id) : '',
|
|
status: m.status != null ? String(m.status).toUpperCase() : 'PENDING',
|
|
rejectionReason: rejectionFromMeta(m),
|
|
lastSyncedAt: now,
|
|
lastError: '',
|
|
...(m.category ? { category: normalizeCategory(m.category) } : {}),
|
|
...(Array.isArray(m.components) && m.components.length ? { components: m.components } : {}),
|
|
},
|
|
},
|
|
);
|
|
updated += 1;
|
|
continue;
|
|
}
|
|
throw e;
|
|
}
|
|
}
|
|
|
|
return { ok: true, metaCount: list.length, updated, created };
|
|
}
|
|
|
|
module.exports = { syncOrganizationTemplates };
|