256 lines
9.3 KiB
TypeScript
256 lines
9.3 KiB
TypeScript
/**
|
|
* Módulo de Sanitización para Títulos y Textos de Publicaciones
|
|
* Garantiza estrictamente que NI en el título NI en el texto de la publicación
|
|
* se incluya el nombre del grupo de WhatsApp, canal, proveedor o términos mayoristas.
|
|
*/
|
|
|
|
// Términos y palabras clave prohibidas por defecto (grupos, bodegas, proveedores y mayoristas)
|
|
export const DEFAULT_FORBIDDEN_TERMS = [
|
|
'difusión jovic store colombia',
|
|
'difusion jovic store colombia',
|
|
'difusion jovic store',
|
|
'difusión jovic store',
|
|
'difusion jovic',
|
|
'difusión jovic',
|
|
'jovic store',
|
|
'exclusive cc mío 3b',
|
|
'exclusive cc mio 3b',
|
|
'cc mío 3b',
|
|
'cc mio 3b',
|
|
'cc mio',
|
|
'cc mío',
|
|
'bodega central mayorista',
|
|
'bodega central',
|
|
'bodega mayorista',
|
|
'proveedor mayorista',
|
|
'calzado mayorista',
|
|
'mayorista cali',
|
|
'mayoristas cali',
|
|
'bodega cali',
|
|
'canal mayorista',
|
|
'grupo mayorista',
|
|
'grupo whatsapp',
|
|
'team emporio',
|
|
'medios y prensa gob cauca',
|
|
'medios y prensa',
|
|
'notificaciones almeira',
|
|
'almeira notificaciones',
|
|
'san andresito',
|
|
'dropshipping colombia',
|
|
'bodegas aliadas',
|
|
];
|
|
|
|
// Regex para detectar patrones de prefijos de proveedor / grupo
|
|
const SUPPLIER_PREFIX_PATTERNS = [
|
|
/(?:^|\n|\s*)(?:proveedor|bodega|distribuidor[a]?|mayorista[s]?|canal|grupo\s*(?:de\s*whatsapp)?)\s*[:\-–—]\s*([^\n,.]+)/gi,
|
|
/(?:^|\n|\s*)(?:de|por|remitente|fuente)\s*[:\-–—]\s*([^\n,.]+)/gi,
|
|
];
|
|
|
|
// Regex para eliminar palabras y conceptos de intermediarios en publicaciones comerciales
|
|
const GENERIC_FORBIDDEN_WORDS_REGEX =
|
|
/\b(proveedor(?:es)?|bodega(?:s)?(?:\s+mayorista)?|distribuidor(?:a)?(?:es)?|mayorista(?:s)?|canal(?:\s+mayorista)?|grupo(?:\s+de)?\s+whatsapp)\b/gi;
|
|
|
|
// Regex para eliminar menciones de costo, precios mayoristas y precios de compra
|
|
const COST_AND_PRICE_PATTERNS = [
|
|
/(?:costo|precio\s*mayorista|precio\s*bodega|precio\s*distribuidor|precio\s*compra|al\s*por\s*mayor|valor\s*mayorista|mayorista)\s*[:\-–—]?\s*(?:\$?\s*[\d.,]+\s*(?:mil|k)?|\d+k|\$\s*\d+)?/gi,
|
|
/(?:^|\n|\s*)(?:costo|precio\s*costo|precio\s*proveedor|precio\s*bodega)\s*[:\-–—]?\s*[^\n]+/gi,
|
|
/(?:💸|💰|💵|💲)\s*[:\-–—]?\s*(?:\$?\s*[\d.,]+\s*(?:mil|k)?|\d+k|\$\s*\d+)/gi,
|
|
/(?:^|\n|\s*)(?:precio|valor)\s*[:\-–—]\s*(?:\$?\s*[\d.,]+\s*(?:mil|k)?|\d+k|\$\s*\d+)/gi,
|
|
/\b\d{2,3}[\.,]\d{3}\b|\b\d{2,3}k\b|\b\d{2,3}\s*mil\b/gi,
|
|
];
|
|
|
|
/**
|
|
* Escapa caracteres especiales para usar en expresiones regulares
|
|
*/
|
|
function escapeRegExp(str: string): string {
|
|
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
}
|
|
|
|
/**
|
|
* Limpia emojis y símbolos decorativos de nombres de grupos
|
|
*/
|
|
export function cleanGroupName(raw: string): string {
|
|
if (!raw) return '';
|
|
return raw
|
|
.replace(/[💎🔥👑❤️🔥🤯🚨✨🌟⚡👟📦🏢📍🏷️💵💰💸💲]/g, '')
|
|
.replace(/\s+/g, ' ')
|
|
.trim();
|
|
}
|
|
|
|
/**
|
|
* Normaliza y compila lista de términos prohibidos
|
|
*/
|
|
function buildForbiddenList(extraForbidden: (string | undefined | null)[] = []): string[] {
|
|
const terms = new Set<string>();
|
|
|
|
DEFAULT_FORBIDDEN_TERMS.forEach((t) => terms.add(t.toLowerCase().trim()));
|
|
|
|
extraForbidden.forEach((item) => {
|
|
if (!item || typeof item !== 'string') return;
|
|
const clean = cleanGroupName(item).toLowerCase().trim();
|
|
if (clean.length >= 3) {
|
|
terms.add(clean);
|
|
}
|
|
const rawLower = item.toLowerCase().trim();
|
|
if (rawLower.length >= 3) {
|
|
terms.add(rawLower);
|
|
}
|
|
});
|
|
|
|
return Array.from(terms).filter((t) => t.length >= 3);
|
|
}
|
|
|
|
/**
|
|
* Sanitiza el TÍTULO / NOMBRE comercial de la publicación
|
|
* - Elimina nombres de grupos de WhatsApp
|
|
* - Elimina nombres de proveedores o bodegas
|
|
* - Elimina costos, precios y menciones a intermediarios
|
|
* - Asegura que el producto tenga un título comercial atractivo y limpio
|
|
*/
|
|
export function sanitizePublicationTitle(
|
|
rawTitle: string,
|
|
extraForbidden: (string | undefined | null)[] = [],
|
|
fallbackBrand: string = 'Sneakers'
|
|
): string {
|
|
if (!rawTitle || typeof rawTitle !== 'string') {
|
|
return `${fallbackBrand} Urban Edition`;
|
|
}
|
|
|
|
let cleaned = rawTitle.trim();
|
|
|
|
// 1. Quitar prefijos de proveedor (ej: "Proveedor: Nike Dunk" -> "Nike Dunk")
|
|
for (const pattern of SUPPLIER_PREFIX_PATTERNS) {
|
|
cleaned = cleaned.replace(pattern, '').trim();
|
|
}
|
|
|
|
// 2. Quitar nombres de grupos y proveedores específicos
|
|
const forbiddenList = buildForbiddenList(extraForbidden);
|
|
for (const term of forbiddenList) {
|
|
const termRegex = new RegExp(`(^|[\\s,;:\\-–—(]|\\[)${escapeRegExp(term)}([\\s,;:\\-–—)\\]]|$)`, 'gi');
|
|
cleaned = cleaned.replace(termRegex, ' ').trim();
|
|
}
|
|
|
|
// 3. Quitar palabras genéricas de intermediarios
|
|
cleaned = cleaned.replace(GENERIC_FORBIDDEN_WORDS_REGEX, ' ').trim();
|
|
|
|
// 4. Quitar estrictamente costos, cifras de precios y menciones monetarias
|
|
for (const pattern of COST_AND_PRICE_PATTERNS) {
|
|
cleaned = cleaned.replace(pattern, ' ').trim();
|
|
}
|
|
|
|
// 5. Limpiar emojis excesivos de inicio, códigos numéricos, precios pegados
|
|
cleaned = cleaned
|
|
.replace(/^[#*_\-•—–~:;,\s]+/, '')
|
|
.replace(/🔥|🔴|‼️|✨|🌸|👑|💎|🛍️|🌟|💸|💰|💵|💲|\*/g, '')
|
|
.replace(/\b\d{2,3}[\.,]\d{3}\b|\b\d{2,3}k\b|\$\s*\d+/gi, '')
|
|
.replace(/dama\s*y\s*caballero|caballero|dama|unisex|recarga|nueva\s*mercanc[ií]a|[uú]ltima\s*unidad/gi, '')
|
|
.replace(/\b(costo|precio|valor)\b/gi, '')
|
|
.replace(/\s+/g, ' ')
|
|
.trim();
|
|
|
|
// 6. Si quedó vacío o demasiado corto o numérico, asignar nombre por defecto limpio
|
|
if (cleaned.length < 3 || /^\d+$/.test(cleaned) || /^[-,.:;\s]+$/.test(cleaned)) {
|
|
return fallbackBrand && fallbackBrand !== 'Urbano / Variado'
|
|
? `${fallbackBrand} Urban Edition`
|
|
: 'Zapatillas Urbanas Streetwear';
|
|
}
|
|
|
|
// 7. Formatear en Capital Case limpio
|
|
return cleaned
|
|
.split(' ')
|
|
.filter(Boolean)
|
|
.map((w) => {
|
|
// Conservar siglas reconocidas en mayúsculas completas (OG, SE, SB, TN, CL, RS-X, GT, etc.)
|
|
if (/^(OG|SE|SB|TN|CL|GT|RS-X|BB|LV|NB|XT-6|9060|550|530|1906R|2002R)$/i.test(w)) {
|
|
return w.toUpperCase();
|
|
}
|
|
return w.charAt(0).toUpperCase() + w.slice(1).toLowerCase();
|
|
})
|
|
.join(' ');
|
|
}
|
|
|
|
/**
|
|
* Sanitiza el TEXTO / DESCRIPCIÓN de la publicación
|
|
* - Elimina menciones a grupos de WhatsApp, enlaces a grupos y canales
|
|
* - Elimina nombres de proveedores, bodegas o intermediarios
|
|
* - Elimina rigurosamente el COSTO del producto, precio mayorista o valor de compra
|
|
* - Elimina contactos directos del proveedor o advertencias dirigidas a revendedores
|
|
*/
|
|
export function sanitizePublicationText(
|
|
rawText: string,
|
|
extraForbidden: (string | undefined | null)[] = [],
|
|
knownSupplierPrice?: number
|
|
): string {
|
|
if (!rawText || typeof rawText !== 'string') return '';
|
|
|
|
let cleaned = rawText;
|
|
|
|
// 1. Quitar enlaces de invitaciones a grupos de WhatsApp
|
|
cleaned = cleaned.replace(/https?:\/\/chat\.whatsapp\.com\/[A-Za-z0-9]+/gi, '');
|
|
|
|
// 2. Quitar teléfonos típicos de proveedores mencionados en el texto
|
|
cleaned = cleaned.replace(/(?:tel[eé]fono|contacto|pedidos\s*proveedor|asesor|bodega)\s*[:\-–—]?\s*(?:\+?57\s*)?3\d{2}[\s.-]?\d{3}[\s.-]?\d{4}/gi, '');
|
|
|
|
// 3. Quitar nombres de grupos y proveedores específicos
|
|
const forbiddenList = buildForbiddenList(extraForbidden);
|
|
for (const term of forbiddenList) {
|
|
const termRegex = new RegExp(`(^|[\\s,;:\\-–—(]|\\[)${escapeRegExp(term)}([\\s,;:\\-–—)\\]]|$)`, 'gi');
|
|
cleaned = cleaned.replace(termRegex, ' ');
|
|
}
|
|
|
|
// 4. Si se conoce el precio de costo numérico, eliminar cualquier aparición de dicha cifra
|
|
if (knownSupplierPrice && knownSupplierPrice > 0) {
|
|
const costStr = knownSupplierPrice.toString();
|
|
const costFormatted = knownSupplierPrice.toLocaleString('es-CO');
|
|
const costInK = `${Math.round(knownSupplierPrice / 1000)}k`;
|
|
const costInMil = `${Math.round(knownSupplierPrice / 1000)}\\s*mil`;
|
|
const costRegex = new RegExp(`\\$?\\s*(?:${escapeRegExp(costStr)}|${escapeRegExp(costFormatted)}|${costInK}|${costInMil})\\b`, 'gi');
|
|
cleaned = cleaned.replace(costRegex, ' ');
|
|
}
|
|
|
|
// 5. Quitar patrones de costos, precios mayoristas y frases de distribuidor
|
|
for (const pattern of COST_AND_PRICE_PATTERNS) {
|
|
cleaned = cleaned.replace(pattern, ' ');
|
|
}
|
|
|
|
// 6. Quitar líneas que sean identificadores de proveedor, grupo o costos
|
|
const lines = cleaned.split('\n');
|
|
const cleanLines = lines.filter((line) => {
|
|
const lower = line.toLowerCase().trim();
|
|
if (
|
|
lower.startsWith('proveedor:') ||
|
|
lower.startsWith('bodega:') ||
|
|
lower.startsWith('canal:') ||
|
|
lower.startsWith('grupo:') ||
|
|
lower.startsWith('costo:') ||
|
|
lower.startsWith('costo') ||
|
|
lower.startsWith('precio mayorista') ||
|
|
lower.startsWith('precio distribuidor') ||
|
|
lower.startsWith('mayorista:') ||
|
|
lower.includes('grupo mayorista') ||
|
|
lower.includes('canal de difusión') ||
|
|
lower.includes('canal de difusion') ||
|
|
lower.includes('difusion jovic') ||
|
|
lower.includes('cc mío 3b') ||
|
|
lower.includes('precio al por mayor') ||
|
|
lower.includes('precio de compra')
|
|
) {
|
|
return false;
|
|
}
|
|
return true;
|
|
});
|
|
|
|
cleaned = cleanLines.join('\n');
|
|
|
|
// 7. Quitar palabras clave genéricas de intermediarios sueltas
|
|
cleaned = cleaned.replace(GENERIC_FORBIDDEN_WORDS_REGEX, ' ');
|
|
|
|
// 8. Normalizar espaciado
|
|
cleaned = cleaned
|
|
.replace(/[ \t]+/g, ' ')
|
|
.replace(/\n{3,}/g, '\n\n')
|
|
.trim();
|
|
|
|
return cleaned;
|
|
}
|