Files
gameno-api/components/settings/smsTemplates.js
T

623 lines
24 KiB
JavaScript

// /components/settings/smsTemplates.js
'use strict';
const SMS_TEMPLATE_DEFS = [
{
key: 'sessionHolding',
label: 'کلاس طبق برنامه',
category: 'اطلاع‌رسانی',
defaultTemplateId: '720661',
envKey: 'SMS_TEMPLATE_SESSION_HOLDING',
defaultText: ` #FULLNAME# عزیز، «#TOPIC#» کلاس #CLASSNAME# در تاریخ #SESSIONDATE# و ساعت #CLASSTIME# طبق برنامه برگزار خواهد شد.
آموزشگاه گام نو
game-no.ir`,
slots: [
{ key: 'fullName', label: 'نام کارآموز', defaultName: 'FULLNAME' },
{ key: 'topic', label: 'موضوع جلسه', defaultName: 'TOPIC' },
{ key: 'className', label: 'نام کلاس', defaultName: 'CLASSNAME' },
{ key: 'sessionDate', label: 'تاریخ جلسه', defaultName: 'SESSIONDATE' },
{ key: 'classTime', label: 'ساعت کلاس', defaultName: 'CLASSTIME' },
{ key: 'courseName', label: 'نام دوره', defaultName: 'courseName' },
{ key: 'classCode', label: 'کد کلاس', defaultName: 'classCode' },
{ key: 'place', label: 'مکان', defaultName: 'place' },
{ key: 'phoneNumber', label: 'شماره همراه', defaultName: 'mobile' }
]
},
{
key: 'certificateIssued',
label: 'صدور گواهی',
category: 'اطلاع‌رسانی',
defaultTemplateId: '854493',
envKey: 'SMS_TEMPLATE_CERTIFICATE_ISSUED',
defaultText: ` #FULLNAME# عزیز، گواهی «#CERTIFICATETITLE#» شما، برای دوره #COURSENAME# صادر شد.
جهت مشاهده و دانلود گواهی به پروفایل خود در وبسایت آموزشگاه مراجعه کنید.
آموزشگاه گام نو
game-no.ir`,
slots: [
{ key: 'fullName', label: 'نام کارآموز', defaultName: 'FULLNAME' },
{ key: 'certificateTitle', label: 'عنوان گواهینامه', defaultName: 'CERTIFICATETITLE' },
{ key: 'courseName', label: 'نام دوره', defaultName: 'COURSENAME' },
{ key: 'certificateCode', label: 'کد گواهینامه', defaultName: 'certificateCode' },
{ key: 'phoneNumber', label: 'شماره همراه', defaultName: 'mobile' }
]
},
{
key: 'sessionCancelled',
label: 'لغو کلاس',
category: 'اطلاع‌رسانی',
defaultTemplateId: '174248',
envKey: 'SMS_TEMPLATE_SESSION_CANCELLED',
defaultText: ` #FULLNAME# عزیز، جلسه «#TOPIC#» کلاس #CLASSNAME# در تاریخ #SESSIONDATE# برگزار نخواهد شد.
آموزشگاه گام نو
game-no.ir`,
slots: [
{ key: 'fullName', label: 'نام کارآموز', defaultName: 'FULLNAME' },
{ key: 'topic', label: 'موضوع جلسه', defaultName: 'TOPIC' },
{ key: 'className', label: 'نام کلاس', defaultName: 'CLASSNAME' },
{ key: 'sessionDate', label: 'تاریخ جلسه', defaultName: 'SESSIONDATE' },
{ key: 'reason', label: 'دلیل لغو', defaultName: 'reason' },
{ key: 'classCode', label: 'کد کلاس', defaultName: 'classCode' },
{ key: 'phoneNumber', label: 'شماره همراه', defaultName: 'mobile' }
]
},
{
key: 'transactionRecorded',
label: 'ثبت تراکنش',
category: 'اطلاع‌رسانی',
defaultTemplateId: '580983',
envKey: 'SMS_TEMPLATE_TRANSACTION_RECORDED',
defaultText: ` #FULLNAME# عزیز، تراکنش #TRANSACTIONCODE# به مبلغ #AMOUNT# تومان ثبت شد.
صورتحساب: #INVOICECODE# — رسید: #RECEIPTNUMBER#
آموزشگاه گام نو
game-no.ir`,
slots: [
{ key: 'fullName', label: 'نام کارآموز', defaultName: 'FULLNAME' },
{ key: 'transactionCode', label: 'کد تراکنش', defaultName: 'TRANSACTIONCODE' },
{ key: 'amount', label: 'مبلغ', defaultName: 'AMOUNT' },
{ key: 'invoiceCode', label: 'کد صورتحساب', defaultName: 'INVOICECODE' },
{ key: 'receiptNumber', label: 'شماره رسید', defaultName: 'RECEIPTNUMBER' },
{ key: 'phoneNumber', label: 'شماره همراه', defaultName: 'mobile' }
]
},
{
key: 'paymentReminder',
label: 'یادآوری پرداخت',
category: 'اطلاع‌رسانی',
defaultTemplateId: '357550',
envKey: 'SMS_TEMPLATE_PAYMENT_REMINDER',
defaultText: ` #FULLNAME# عزیز، یادآوری پرداخت:
صورتحساب #INVOICECODE# — مبلغ باقیمانده: #AMOUNT# تومان
سررسید: #DUEDATE#
دوره: #COURSE#
آموزشگاه گام نو
game-no.ir`,
slots: [
{ key: 'fullName', label: 'نام کارآموز', defaultName: 'FULLNAME' },
{ key: 'invoiceCode', label: 'کد صورتحساب', defaultName: 'INVOICECODE' },
{ key: 'amount', label: 'مبلغ باقیمانده', defaultName: 'AMOUNT' },
{ key: 'dueDate', label: 'تاریخ سررسید', defaultName: 'DUEDATE' },
{ key: 'course', label: 'نام دوره', defaultName: 'COURSE' },
{ key: 'phoneNumber', label: 'شماره همراه', defaultName: 'mobile' }
]
},
{
key: 'paymentStatusChanged',
label: 'وضعیت صورتحساب',
category: 'وضعیت سفارش',
defaultTemplateId: '270174',
envKey: 'SMS_TEMPLATE_PAYMENT_STATUS_CHANGED',
defaultText: ` #FULLNAME# عزیز، وضعیت صورتحساب #INVOICECODE# به «#STATUS#» تغییر کرد.
مبلغ: #AMOUNT# تومان — دوره: #COURSE#
آموزشگاه گام نو
game-no.ir`,
slots: [
{ key: 'fullName', label: 'نام کارآموز', defaultName: 'FULLNAME' },
{ key: 'invoiceCode', label: 'کد صورتحساب', defaultName: 'INVOICECODE' },
{ key: 'status', label: 'وضعیت', defaultName: 'STATUS' },
{ key: 'amount', label: 'مبلغ', defaultName: 'AMOUNT' },
{ key: 'course', label: 'نام دوره', defaultName: 'COURSE' },
{ key: 'phoneNumber', label: 'شماره همراه', defaultName: 'mobile' }
]
},
{
key: 'accountCreated',
label: 'اطلاعات اکانت',
category: 'اطلاع‌رسانی',
defaultTemplateId: '250973',
envKey: 'SMS_TEMPLATE_ACCOUNT_CREATED',
defaultText: ` کارآموز گرامی، یک حساب کاربری برای شما در وبسایت آموزشگاه گام نو ایجاد شد.
نام کاربری: #USER#
رمز عبور: #PASSWORD#
game-no.ir`,
slots: [
{ key: 'username', label: 'نام کاربری', defaultName: 'USER' },
{ key: 'password', label: 'رمز عبور', defaultName: 'PASSWORD' },
{ key: 'fullName', label: 'نام کاربر', defaultName: 'name' },
{ key: 'phoneNumber', label: 'شماره همراه', defaultName: 'mobile' },
{ key: 'userCode', label: 'کد کاربری', defaultName: 'userCode' }
]
},
{
key: 'invoiceCreated',
label: 'پرداخت',
category: 'اطلاع‌رسانی',
defaultTemplateId: '580892',
envKey: 'SMS_TEMPLATE_INVOICE_CREATED',
defaultText: ` کارآموز عزیز، #FULLNAME#، یک صورتحساب به مبلغ #PAYMENT_PRICE# تومان، بابت دوره ی #COURSE# برای شما ایجاد شده است.
آموزشگاه گام نو
game-no.ir`,
slots: [
{ key: 'fullName', label: 'نام کارآموز', defaultName: 'FULLNAME' },
{ key: 'amount', label: 'مبلغ', defaultName: 'PAYMENT_PRICE' },
{ key: 'course', label: 'نام دوره', defaultName: 'COURSE' },
{ key: 'phoneNumber', label: 'شماره همراه', defaultName: 'MOBILE' },
{ key: 'classStartDate', label: 'تاریخ شروع کلاس', defaultName: 'classStartDate' },
{ key: 'classDays', label: 'روزهای برگزاری', defaultName: 'classDays' },
{ key: 'courseTime', label: 'ساعت دوره', defaultName: 'courseTime' },
{ key: 'invoiceCode', label: 'کد صورتحساب', defaultName: 'invoiceCode' }
]
},
{
key: 'classRegistered',
label: 'ثبت نام دوره',
category: 'اطلاع‌رسانی',
defaultTemplateId: '910210',
envKey: 'SMS_TEMPLATE_CLASS_REGISTERED',
defaultText: ` کارآموز عزیز، #FULLNAME#، ثبت نام شما در دوره #CLASS# انجام شد.
هر هفته #CLASSDAYS#، #CLASSTIME#
آموزشگاه گام نو
game-no.ir`,
slots: [
{ key: 'fullName', label: 'نام کارآموز', defaultName: 'FULLNAME' },
{ key: 'className', label: 'نام کلاس', defaultName: 'CLASS' },
{ key: 'classDays', label: 'روزهای برگزاری', defaultName: 'CLASSDAYS' },
{ key: 'courseTime', label: 'ساعت دوره', defaultName: 'CLASSTIME' },
{ key: 'courseName', label: 'نام دوره', defaultName: 'courseName' },
{ key: 'phoneNumber', label: 'شماره همراه', defaultName: 'mobile' },
{ key: 'classStartDate', label: 'تاریخ شروع کلاس', defaultName: 'classStartDate' },
{ key: 'classCode', label: 'کد کلاس', defaultName: 'classCode' }
]
},
{
key: 'classPlanProfessor',
label: 'برنامه کلاس به استاد',
category: 'اطلاع‌رسانی',
defaultTemplateId: '',
envKey: 'SMS_TEMPLATE_CLASS_PLAN_PROFESSOR',
defaultText: `با سلام و وقت بخیر، استاد #professorName#،
برنامه کلاس #className# شما به شرح زیر می باشد:
#classDays#، #classTimes#
از #classStartDate# الی #classEndDate#`,
slots: [
{ key: 'professorName', label: 'نام استاد', defaultName: 'professorName' },
{ key: 'className', label: 'نام کلاس', defaultName: 'className' },
{ key: 'classDays', label: 'روزهای برگزاری', defaultName: 'classDays' },
{ key: 'classTimes', label: 'ساعت برگزاری', defaultName: 'classTimes' },
{ key: 'classStartDate', label: 'تاریخ شروع', defaultName: 'classStartDate' },
{ key: 'classEndDate', label: 'تاریخ پایان', defaultName: 'classEndDate' },
{ key: 'phoneNumber', label: 'شماره همراه', defaultName: 'mobile' }
]
},
{
key: 'passwordReset',
label: 'بازنشانی رمز عبور',
category: 'اطلاع‌رسانی',
defaultTemplateId: '250973',
envKey: 'SMS_TEMPLATE_PASSWORD_RESET',
defaultText: ` کارآموز گرامی، رمز عبور حساب کاربری شما در وبسایت آموزشگاه گام نو بازنشانی شد.
نام کاربری: #USER#
رمز عبور: #PASSWORD#
game-no.ir`,
slots: [
{ key: 'username', label: 'نام کاربری', defaultName: 'USER' },
{ key: 'password', label: 'رمز عبور', defaultName: 'PASSWORD' },
{ key: 'fullName', label: 'نام کاربر', defaultName: 'name' },
{ key: 'phoneNumber', label: 'شماره همراه', defaultName: 'mobile' },
{ key: 'userCode', label: 'کد کاربری', defaultName: 'userCode' }
]
},
{
key: 'classReminder',
label: 'یادآوری کلاس',
category: 'اطلاع‌رسانی',
defaultTemplateId: '',
envKey: 'SMS_TEMPLATE_CLASS_REMINDER',
defaultText: ` #FULLNAME# عزیز، یادآوری کلاس «#CLASSNAME#» در تاریخ #SESSIONDATE# ساعت #CLASSTIME#.
مکان: #PLACE#
آموزشگاه گام نو
game-no.ir`,
slots: [
{ key: 'className', label: 'نام کلاس', defaultName: 'CLASSNAME' },
{ key: 'topic', label: 'موضوع جلسه', defaultName: 'topic' },
{ key: 'classTime', label: 'ساعت کلاس', defaultName: 'CLASSTIME' },
{ key: 'sessionDate', label: 'تاریخ جلسه', defaultName: 'SESSIONDATE' },
{ key: 'place', label: 'مکان', defaultName: 'PLACE' },
{ key: 'fullName', label: 'نام کاربر', defaultName: 'FULLNAME' },
{ key: 'phoneNumber', label: 'شماره همراه', defaultName: 'mobile' },
{ key: 'classStartDate', label: 'تاریخ شروع کلاس', defaultName: 'classStartDate' },
{ key: 'classDays', label: 'روزهای برگزاری', defaultName: 'classDays' },
{ key: 'courseTime', label: 'ساعت دوره', defaultName: 'courseTime' },
{ key: 'classCode', label: 'کد کلاس', defaultName: 'classCode' }
]
},
{
key: 'classRequestApproved',
label: 'تأیید درخواست تشکیل کلاس',
category: 'ثبت‌نام و درخواست‌ها',
defaultTemplateId: '',
envKey: 'SMS_TEMPLATE_CLASS_REQUEST_APPROVED',
defaultEnabled: false,
defaultText: ` #FULLNAME# عزیز، درخواست شما برای تشکیل کلاس #COURSENAME# تأیید شد.
آموزشگاه گام نو
game-no.ir`,
slots: [
{ key: 'fullName', label: 'نام کارآموز', defaultName: 'FULLNAME' },
{ key: 'courseName', label: 'نام دوره', defaultName: 'COURSENAME' },
{ key: 'classCode', label: 'کد کلاس', defaultName: 'classCode' },
{ key: 'phoneNumber', label: 'شماره همراه', defaultName: 'mobile' }
]
},
{
key: 'classRequestRejected',
label: 'رد درخواست ثبت‌نام',
category: 'ثبت‌نام و درخواست‌ها',
defaultTemplateId: '',
envKey: 'SMS_TEMPLATE_CLASS_REQUEST_REJECTED',
defaultEnabled: false,
defaultText: ` #FULLNAME# عزیز، درخواست ثبت‌نام شما برای دوره #COURSENAME# مورد پذیرش قرار نگرفت.
دلیل: #REASON#
آموزشگاه گام نو
game-no.ir`,
slots: [
{ key: 'fullName', label: 'نام کارآموز', defaultName: 'FULLNAME' },
{ key: 'courseName', label: 'نام دوره', defaultName: 'COURSENAME' },
{ key: 'reason', label: 'دلیل', defaultName: 'REASON' },
{ key: 'registrationCode', label: 'کد درخواست', defaultName: 'registrationCode' },
{ key: 'phoneNumber', label: 'شماره همراه', defaultName: 'mobile' }
]
},
{
key: 'pendingRegistration',
label: 'دریافت درخواست ثبت‌نام',
category: 'ثبت‌نام و درخواست‌ها',
defaultTemplateId: '',
envKey: 'SMS_TEMPLATE_PENDING_REGISTRATION',
defaultEnabled: false,
defaultText: ` #FULLNAME# عزیز، درخواست ثبت‌نام شما در کلاس #CLASSNAME# با کد پیگیری #REGISTRATIONCODE# دریافت شد.
آموزشگاه گام نو
game-no.ir`,
slots: [
{ key: 'fullName', label: 'نام کارآموز', defaultName: 'FULLNAME' },
{ key: 'className', label: 'نام کلاس', defaultName: 'CLASSNAME' },
{ key: 'registrationCode', label: 'کد درخواست', defaultName: 'REGISTRATIONCODE' },
{ key: 'phoneNumber', label: 'شماره همراه', defaultName: 'mobile' }
]
}
];
const SMS_TEMPLATE_KEYS = SMS_TEMPLATE_DEFS.map((def) => def.key);
const envFallbackFor = (def) => {
if (!def?.envKey) return '';
return String(process.env[def.envKey] || '').trim();
};
const sanitizeTemplateId = (value) => {
if (value == null) return '';
const digits = String(value).trim();
if (!digits) return '';
if (!/^\d{1,30}$/.test(digits)) return null;
return digits;
};
const sanitizeVariableName = (value) => {
if (value == null) return '';
const name = String(value).trim().replace(/^#+|#+$/g, '').trim();
if (!name) return '';
if (/[\r\n\t\0<>"'`]/.test(name)) return null;
if (name.length > 100) return null;
return name;
};
const escapeRegExp = (string) => {
return String(string).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
};
const defaultVariablesList = (def) => {
return (def?.slots || []).map((slot) => ({
slot: slot.key,
name: slot.defaultName
}));
};
const emptyTemplateEntry = (def) => ({
enabled: def ? (def.defaultEnabled !== false) : true,
templateId: def?.defaultTemplateId || '',
category: def?.category || 'اطلاع‌رسانی',
text: def?.defaultText || '',
variables: defaultVariablesList(def)
});
const toVariableItem = (item, fallbackSlot = '') => {
if (item == null) return null;
if (typeof item === 'object') {
const slot = String(item.slot || fallbackSlot || '').trim();
if (!slot) return null;
return {
slot,
name: String(item.name || '').trim()
};
}
const slot = String(fallbackSlot || '').trim();
if (!slot) return null;
return {
slot,
name: String(item).trim()
};
};
const normalizeVariablesInput = (rawVariables, def) => {
if (rawVariables == null) {
return def ? defaultVariablesList(def) : [];
}
if (Array.isArray(rawVariables)) {
return rawVariables.map((item) => toVariableItem(item)).filter(Boolean);
}
if (typeof rawVariables === 'object') {
return Object.entries(rawVariables)
.map(([key, value]) => toVariableItem(value, key))
.filter(Boolean);
}
return def ? defaultVariablesList(def) : [];
};
const normalizeStoredEntry = (raw, def) => {
const defaultEnabled = def ? (def.defaultEnabled !== false) : true;
const defaultText = def?.defaultText || '';
const defaultTemplateId = def?.defaultTemplateId || '';
const defaultCategory = def?.category || 'اطلاع‌رسانی';
if (raw == null || raw === '') {
return {
enabled: defaultEnabled,
templateId: defaultTemplateId,
category: defaultCategory,
text: defaultText,
variables: def ? defaultVariablesList(def) : []
};
}
if (typeof raw === 'string' || typeof raw === 'number') {
return {
enabled: true,
templateId: String(raw),
category: defaultCategory,
text: defaultText,
variables: def ? defaultVariablesList(def) : []
};
}
if (typeof raw !== 'object') {
return {
enabled: defaultEnabled,
templateId: defaultTemplateId,
category: defaultCategory,
text: defaultText,
variables: def ? defaultVariablesList(def) : []
};
}
const variables = Object.prototype.hasOwnProperty.call(raw, 'variables')
? normalizeVariablesInput(raw.variables, def)
: (def ? defaultVariablesList(def) : []);
const enabled = raw.enabled !== undefined
? Boolean(raw.enabled)
: defaultEnabled;
const templateId = raw.templateId != null && String(raw.templateId).trim() !== ''
? String(raw.templateId).trim()
: defaultTemplateId;
const text = raw.text != null && String(raw.text).trim() !== ''
? String(raw.text)
: defaultText;
const category = raw.category || defaultCategory;
return {
enabled,
templateId,
category,
text,
variables
};
};
const parseIncomingVariables = (rawVariables, def) => {
if (rawVariables === undefined) return undefined;
return normalizeVariablesInput(rawVariables, def);
};
const mergeTemplateEntry = (def, storedRaw, incomingRaw = null) => {
const storedEntry = normalizeStoredEntry(storedRaw, def);
let templateId = storedEntry.templateId || envFallbackFor(def) || def?.defaultTemplateId || '';
let variables = storedEntry.variables;
let enabled = storedEntry.enabled;
let text = storedEntry.text || def?.defaultText || '';
let category = storedEntry.category || def?.category || 'اطلاع‌رسانی';
if (incomingRaw) {
if (incomingRaw.enabled !== undefined) {
enabled = Boolean(incomingRaw.enabled);
}
if (incomingRaw.text !== undefined) {
text = String(incomingRaw.text || '');
}
if (incomingRaw.category !== undefined) {
category = String(incomingRaw.category || '');
}
if (incomingRaw.templateId !== undefined) {
const sanitized = sanitizeTemplateId(incomingRaw.templateId);
if (sanitized === null) {
const error = new Error(`Invalid SMS template ID for ${def?.key || 'template'}`);
error.code = 'INVALID_TEMPLATE_ID';
error.field = def?.key;
throw error;
}
templateId = sanitized;
}
if (incomingRaw.variables !== undefined) {
const parsed = parseIncomingVariables(incomingRaw.variables, def) || [];
for (const item of parsed) {
const sanitized = sanitizeVariableName(item.name);
if (item.name && sanitized === null) {
const error = new Error(`Invalid SMS variable name "${item.name}"`);
error.code = 'INVALID_VARIABLE_NAME';
error.field = `${def?.key}.${item.slot}`;
error.rawName = item.name;
throw error;
}
}
variables = parsed;
}
} else if (!storedEntry.templateId) {
templateId = envFallbackFor(def) || def?.defaultTemplateId || '';
}
return {
enabled,
templateId,
category,
text,
variables: resolveVariablesList(def, variables)
};
};
const resolveVariablesList = (def, storedVariables) => {
if (!storedVariables) {
return defaultVariablesList(def);
}
return normalizeVariablesInput(storedVariables, def)
.map((item) => {
const slot = String(item.slot || '').trim();
if (!slot) return null;
const slotDef = (def?.slots || []).find((s) => s.key === slot);
const rawName = item.name !== undefined && String(item.name).trim() !== ''
? String(item.name).trim()
: slotDef?.defaultName || '';
const sanitized = sanitizeVariableName(rawName);
return {
slot,
name: sanitized || slotDef?.defaultName || slot
};
})
.filter(Boolean);
};
const isPriceSlot = (slot = '', name = '') => {
const s = String(slot).toLowerCase();
const n = String(name).toLowerCase();
return s.includes('amount') || s.includes('price') || s.includes('tuition') || s.includes('fee')
|| n.includes('amount') || n.includes('price') || n.includes('tuition') || n.includes('fee') || n.includes('cost');
};
const formatPriceValue = (val) => {
if (val === null || val === undefined || val === '') return '';
const str = String(val).trim();
const rawNum = Number(str.replace(/,/g, ''));
if (!Number.isNaN(rawNum) && Number.isFinite(rawNum)) {
return rawNum.toLocaleString('en-US');
}
return str;
};
const buildSmsParameters = (variables, valuesBySlot = {}) => {
return normalizeVariablesInput(variables, null)
.map((item) => {
const name = sanitizeVariableName(item?.name);
if (!name) return null;
const slot = item?.slot;
let value = slot ? valuesBySlot?.[slot] : '';
if (isPriceSlot(slot, name) && value != null && value !== '') {
value = formatPriceValue(value);
}
return {
name,
value: String(value ?? '')
};
})
.filter(Boolean);
};
/**
* Renders the exact text of an SMS template by replacing variable placeholders (e.g. #FULLNAME# or #className#)
* with the corresponding values provided in slotValues.
*/
const renderSmsText = (templateText, variables = [], slotValues = {}) => {
if (!templateText || typeof templateText !== 'string') return '';
let result = templateText;
// 1. Replace defined template variables (mapped by slot and variable name in sms.ir)
if (Array.isArray(variables)) {
for (const item of variables) {
if (!item || !item.name) continue;
const varName = String(item.name).trim().replace(/^#+|#+$/g, '');
if (!varName) continue;
const slotKey = item.slot;
let val = slotKey !== undefined && slotValues[slotKey] !== undefined ? slotValues[slotKey] : '';
if (isPriceSlot(slotKey, varName) && val != null && val !== '') {
val = formatPriceValue(val);
}
const pattern = new RegExp(`#${escapeRegExp(varName)}#`, 'gi');
result = result.replace(pattern, String(val ?? ''));
}
}
// 2. Direct slot replacements (e.g., #professorName#, #className#, #amount#, #fullName#)
if (slotValues && typeof slotValues === 'object') {
for (const [slotKey, rawVal] of Object.entries(slotValues)) {
if (!slotKey) continue;
let val = rawVal;
if (isPriceSlot(slotKey, slotKey) && val != null && val !== '') {
val = formatPriceValue(val);
}
const pattern = new RegExp(`#${escapeRegExp(slotKey)}#`, 'gi');
result = result.replace(pattern, String(val ?? ''));
}
}
return result;
};
module.exports = {
SMS_TEMPLATE_DEFS,
SMS_TEMPLATE_KEYS,
envFallbackFor,
sanitizeTemplateId,
sanitizeVariableName,
defaultVariablesList,
emptyTemplateEntry,
normalizeStoredEntry,
parseIncomingVariables,
mergeTemplateEntry,
resolveVariablesList,
buildSmsParameters,
renderSmsText,
formatPriceValue
};