Saludos y fechas especiales
No olvides más un cumpleaños. Cada día recibes un correo con los saludos pendientes de tu equipo, tus amigos y tu familia, con el mensaje listo para enviar por WhatsApp.
- Gratis
- Sin registro
- Tus datos quedan en tu cuenta de Google
- Instalación: unos 10 minutos
Cómo funciona
Tres pasos en el día a día. La instalación se hace una sola vez.
- 1
Registras a tus contactos
Nombre, celular, fecha de nacimiento y relación: colaborador, amigo o familiar.
- 2
Recibes un aviso cada día
A la hora que elijas llega un correo, solo cuando hay saludos pendientes.
- 3
Envías con un toque
El enlace abre WhatsApp con el mensaje ya escrito. Lo revisas y presionas Enviar.
Qué incluye
- Cumpleaños de colaboradores, amigos y familiares.
- Aniversarios laborales del equipo.
- Fechas especiales ya cargadas: Año Nuevo, Día del Trabajo, Día Internacional de la Mujer, Día del Padre, Día de la Madre, Día de la Amistad, Día de la Mujer Boliviana, Día Internacional del Hombre y Navidad.
- Mensajes con emoticones que puedes editar a tu gusto.
- Tratamiento según género: "Estimada" o "Estimado", "Querida" o "Querido".
- Un solo mensaje para tu grupo de WhatsApp en las fechas generales.
Estimado(a):
Estos son los saludos de hoy. Presiona el enlace para abrir WhatsApp con el mensaje listo y luego presiona Enviar.
Equipo (colaboradores)
🌹💐 Estimados colaboradores: saludamos a todas las madres del equipo en su día…
Abrir WhatsApp
🎂🎉 Estimada Ana: hoy es un día muy especial. Te deseamos un feliz cumpleaños…
Abrir WhatsApp
Familiares
🌹💐 Querida Rosa: ¡feliz Día de la Madre! Gracias por tu cariño…
Abrir WhatsApp
Antes de empezar, necesitas
- Una cuenta de Google La misma que usas para Gmail o Google Drive.
- WhatsApp en tu celular Sirve WhatsApp o WhatsApp Business.
- Una computadora Para instalarla. El uso diario es desde el celular.
- Unos 10 minutos Es una instalación única.
Video tutorial
Guía de instalación
Siete pasos, sin saber programar. Hazlos desde una computadora y en el orden indicado.
-
Crea una hoja de cálculo nueva
Entra a sheets.new con tu cuenta de Google. Se abre una hoja en blanco. Ponle un nombre, por ejemplo Saludos y fechas.
-
Abre el editor de Apps Script
En el menú de la hoja elige Extensiones → Apps Script. Se abre una pestaña nueva con un archivo llamado Código.gs que trae unas líneas de ejemplo. Selecciona todo con Ctrl + A y bórralo.
-
Pega el código
Copia el código con el botón, pégalo en el editor con Ctrl + V y guarda con Ctrl + S. Si Google pide un nombre para el proyecto, escribe Saludos.
/** * HERRAMIENTA DE SALUDOS Y FECHAS ESPECIALES (Google Sheets + Apps Script) * Creada por CSEPI (c-sepi.com). Uso gratuito. * * Qué hace: crea en tu hoja de cálculo las pestañas y columnas necesarias y cada día te envía, * a tu propio correo, los saludos pendientes (cumpleaños, aniversarios y fechas especiales) * con un enlace que abre WhatsApp con el mensaje ya escrito. * * Privacidad: todos los datos quedan en tu cuenta de Google. El script no envía información * a terceros ni guarda contraseñas. Solo usa tu correo para enviarte el aviso diario. * * Uso: pega este código en Extensiones > Apps Script, guarda, recarga la hoja y usa el menú * "Saludos" > "1. Instalar herramienta". * * Sin garantía: se ofrece "tal cual", bajo responsabilidad del usuario. Pruébala primero con * datos de ejemplo. Más herramientas gratuitas: https://www.c-sepi.com/herramientas */ var TZ = Session.getScriptTimeZone(); var HOJA_CONTACTOS = 'Contactos'; var HOJA_FECHAS = 'Fechas especiales'; var HOJA_CONFIG = 'Configuración'; var HOJA_LOG = 'Registro de avisos'; var RELACIONES = ['Colaborador', 'Familiar', 'Amigo']; var TITULOS = { 'Colaborador': 'Equipo (colaboradores)', 'Familiar': 'Familiares', 'Amigo': 'Amigos' }; var COLOR_ENCABEZADO = '#1F3864'; var ULTIMA_FILA_CONTACTOS = 301; var ULTIMA_FILA_FECHAS = 80; /* ------------------------------------------------------------------ */ /* MENÚ */ /* ------------------------------------------------------------------ */ function onOpen() { SpreadsheetApp.getUi() .createMenu('Saludos') .addItem('1. Instalar herramienta (crea hojas y columnas)', 'instalarHerramienta') .addItem('2. Activar aviso diario', 'instalarActivador') .addSeparator() .addItem('Enviar aviso de hoy ahora (prueba)', 'enviarAvisoDiario') .addItem('Quitar aviso diario', 'quitarActivador') .addToUi(); } /* ------------------------------------------------------------------ */ /* INSTALACIÓN: CREA PESTAÑAS, COLUMNAS, LISTAS Y MENSAJES */ /* ------------------------------------------------------------------ */ /** * Crea las cuatro pestañas. Si una pestaña ya existe, no la toca, * para no borrar datos que ya hayas cargado. */ function instalarHerramienta() { var ss = SpreadsheetApp.getActiveSpreadsheet(); var creadas = []; var omitidas = []; var pasos = [ [HOJA_CONTACTOS, crearContactos_], [HOJA_FECHAS, crearFechas_], [HOJA_CONFIG, crearConfig_], [HOJA_LOG, crearRegistro_] ]; pasos.forEach(function (p) { if (ss.getSheetByName(p[0])) { omitidas.push(p[0]); } else { p[1](ss); creadas.push(p[0]); } }); quitarHojaVacia_(ss); var contactos = ss.getSheetByName(HOJA_CONTACTOS); if (contactos) ss.setActiveSheet(contactos); var msg = ''; if (creadas.length) msg += 'Pestañas creadas: ' + creadas.join(', ') + '.\n'; if (omitidas.length) msg += 'Ya existían y no se modificaron: ' + omitidas.join(', ') + '.\n'; msg += '\nSiguiente paso: abre la pestaña Configuración, revisa tu correo y tu firma, ' + 'y luego usa el menú Saludos > 2. Activar aviso diario.'; SpreadsheetApp.getUi().alert('Instalación terminada', msg, SpreadsheetApp.getUi().ButtonSet.OK); } function estiloEncabezado_(rango) { rango.setFontFamily('Arial').setFontSize(10).setFontWeight('bold').setFontColor('#FFFFFF') .setBackground(COLOR_ENCABEZADO).setHorizontalAlignment('center').setVerticalAlignment('middle').setWrap(true); } /** * Escribe las fórmulas de las columnas L y M. Primero con coma como separador (formato estándar de * Apps Script); si la región de la hoja exige punto y coma y aparece #ERROR!, las reescribe con ";". */ function aplicarFormulasContactos_(h, n) { function construir(sep) { var filas = []; for (var r = 2; r <= ULTIMA_FILA_CONTACTOS; r++) { var f = 'F' + r; var g = 'G' + r; var b = 'B' + r; var fecha = function (anio) { return 'DATE(' + anio + sep + 'MONTH(' + f + ')' + sep + 'DAY(' + f + '))'; }; filas.push([ '=IF(' + f + '=""' + sep + '""' + sep + 'IF(' + fecha('YEAR(TODAY())') + '>=TODAY()' + sep + fecha('YEAR(TODAY())') + sep + fecha('YEAR(TODAY())+1') + '))', '=IF(OR(' + g + '=""' + sep + b + '<>"Colaborador")' + sep + '""' + sep + 'DATEDIF(' + g + sep + 'TODAY()' + sep + '"Y"))' ]); } return filas; } var rango = h.getRange(2, 12, n, 2); rango.setFormulas(construir(',')); SpreadsheetApp.flush(); var prueba = String(h.getRange(2, 12).getDisplayValue()) + String(h.getRange(3, 13).getDisplayValue()); if (prueba.indexOf('#ERROR') !== -1) { rango.setFormulas(construir(';')); SpreadsheetApp.flush(); } } function listaValidacion_(valores) { return SpreadsheetApp.newDataValidation().requireValueInList(valores, true).setAllowInvalid(false).build(); } function crearContactos_(ss) { var h = ss.insertSheet(HOJA_CONTACTOS); var enc = ['Nombre completo', 'Relación (Colaborador/Amigo/Familiar)', 'Cargo o parentesco', 'Celular (con código de país, solo dígitos)', 'Correo electrónico', 'Fecha de nacimiento', 'Fecha de ingreso (solo colaboradores)', 'Género (F/M)', 'Es madre o padre (Sí/No)', 'Consentimiento para saludos (Sí/No, obligatorio en colaboradores)', 'Activo (Sí/No)', 'Próximo cumpleaños', 'Años de servicio']; var anchos = [230, 150, 150, 170, 200, 110, 130, 80, 110, 170, 80, 120, 100]; var n = ULTIMA_FILA_CONTACTOS - 1; h.getRange(1, 1, 1, enc.length).setValues([enc]); estiloEncabezado_(h.getRange(1, 1, 1, enc.length)); h.setRowHeight(1, 62); anchos.forEach(function (w, i) { h.setColumnWidth(i + 1, w); }); h.getRange(2, 1, n, enc.length).setFontFamily('Arial').setFontSize(10); h.getRange(2, 4, n, 1).setNumberFormat('@'); // el celular se guarda como texto h.getRange(2, 6, n, 2).setNumberFormat('dd/mm/yyyy'); h.getRange(2, 12, n, 1).setNumberFormat('dd/mm/yyyy'); h.getRange(2, 13, n, 1).setNumberFormat('0'); h.getRange(2, 6, n, 8).setHorizontalAlignment('center'); // Fila de ejemplo: está inactiva, por lo que no genera saludos. Reemplázala por tu primer contacto. h.getRange(2, 1, 1, 11).setValues([['Ejemplo Apellido Apellido', 'Colaborador', 'Contador', '59170000000', 'ejemplo@correo.com', new Date(1990, 5, 15), new Date(2023, 2, 1), 'F', 'No', 'Sí', 'No']]); // Columnas calculadas: próximo cumpleaños y años de servicio. aplicarFormulasContactos_(h, n); h.getRange(2, 2, n, 1).setDataValidation(listaValidacion_(['Colaborador', 'Amigo', 'Familiar'])); h.getRange(2, 8, n, 1).setDataValidation(listaValidacion_(['F', 'M'])); h.getRange(2, 9, n, 3).setDataValidation(listaValidacion_(['Sí', 'No'])); var fechaValida = SpreadsheetApp.newDataValidation().requireDate().setAllowInvalid(false) .setHelpText('Ingresa una fecha válida, por ejemplo 15/06/1990.').build(); h.getRange(2, 6, n, 2).setDataValidation(fechaValida); h.setFrozenRows(1); h.setFrozenColumns(1); } /** Mensajes iniciales. Usa {estimado}, {querido}, {nombre}, {años} y {firma}. */ function datosFechas_() { var C = 'Colaborador', A = 'Amigo', F = 'Familiar'; return [ ['Cumpleaños', '', '', C, 'Según ficha', '🎂🎉 {estimado} {nombre}: hoy es un día muy especial. Te deseamos un feliz cumpleaños, con salud, alegría y muchos éxitos en tu vida personal y profesional. 🥳 Gracias por formar parte del equipo. 🎁 Un abrazo. {firma}', 'Sí', 'Individual'], ['Aniversario laboral', '', '', C, 'Según ficha', '🎊👏 {estimado} {nombre}: hoy se cumplen {años} año(s) desde tu incorporación al equipo. Gracias por tu compromiso y tu trabajo. 💼 Te deseamos muchos éxitos en esta nueva etapa. 🌟 {firma}', 'Sí', 'Individual'], ['Año Nuevo', 1, 1, C, 'Todos', '🎆🥂 Estimados colaboradores: les deseamos un próspero Año Nuevo, lleno de salud, bienestar y nuevos logros. ✨ Gracias por su trabajo durante el año que termina. 🙏 {firma}', 'Sí', 'Grupo'], ['Día del Trabajo', 1, 5, C, 'Todos', '💪🛠️ Estimados colaboradores: en este Día del Trabajo queremos reconocer el esfuerzo y la dedicación de todo el equipo. 👏 Gracias por su aporte diario. 🤝 Saludos cordiales. {firma}', 'Sí', 'Grupo'], ['Día Internacional de la Mujer', 8, 3, C, 'Mujeres', '💜🌷 Estimados colaboradores: en este Día Internacional de la Mujer saludamos y reconocemos el talento, el trabajo y el aporte de todas las mujeres del equipo. 👏 Felicidades. ✨ {firma}', 'Sí', 'Grupo'], ['Día del Padre', 19, 3, C, 'Padres', '👨👧👦💙 Estimados colaboradores: saludamos a todos los padres del equipo en su día y les deseamos una jornada llena de cariño junto a su familia. 🎁 Felicidades. 🤗 {firma}', 'Sí', 'Grupo'], ['Día de la Madre', 27, 5, C, 'Madres', '🌹💐 Estimados colaboradores: saludamos a todas las madres del equipo en su día, con deseos de salud, felicidad y mucho cariño junto a los suyos. 🥰 Felicidades. ❤️ {firma}', 'Sí', 'Grupo'], ['Día de la Amistad', 21, 9, C, 'Todos', '🤝💛 Estimados colaboradores: en el Día de la Amistad agradecemos el compañerismo y la buena relación que compartimos en el equipo. 😊 Un saludo cordial. 🌟 {firma}', 'Sí', 'Grupo'], ['Día de la Mujer Boliviana', 11, 10, C, 'Mujeres', '🇧🇴💐 Estimados colaboradores: en el Día de la Mujer Boliviana saludamos y reconocemos el trabajo y el aporte de todas las mujeres del equipo. 👏 Felicidades. 🌺 {firma}', 'Sí', 'Grupo'], ['Día Internacional del Hombre', 19, 11, C, 'Hombres', '💙🙌 Estimados colaboradores: en el Día Internacional del Hombre saludamos a todos los hombres del equipo y agradecemos su trabajo. 👏 Felicidades. {firma}', 'Sí', 'Grupo'], ['Navidad', 25, 12, C, 'Todos', '🎄✨ Estimados colaboradores: les deseamos una feliz Navidad junto a sus familias, con paz, salud y unión. 🎁 Gracias por su trabajo durante este año. ❤️ {firma}', 'Sí', 'Grupo'], ['Cumpleaños', '', '', A, 'Según ficha', '🎂🥳 {querido} {nombre}: ¡feliz cumpleaños! Que este nuevo año de vida te traiga salud, alegría y muchos momentos felices. Gracias por tu amistad. 🎁🎈 Un abrazo grande. {firma}', 'Sí', 'Individual'], ['Año Nuevo', 1, 1, A, 'Todos', '🎆🥂 {querido} {nombre}: ¡feliz Año Nuevo! Te deseo un año lleno de salud, felicidad y nuevos logros. ✨ Un abrazo. {firma}', 'Sí', 'Individual'], ['Día de la Amistad', 21, 9, A, 'Todos', '🤝💛 {querido} {nombre}: ¡feliz Día de la Amistad! Gracias por tu amistad y por los buenos momentos compartidos. 😊 Un abrazo. {firma}', 'Sí', 'Individual'], ['Navidad', 25, 12, A, 'Todos', '🎄🎁 {querido} {nombre}: ¡feliz Navidad! Que la pases en paz y junto a quienes más quieres. ❤️ Un abrazo. {firma}', 'Sí', 'Individual'], ['Cumpleaños', '', '', F, 'Según ficha', '🎂🥳 {querido} {nombre}: ¡feliz cumpleaños! Que tengas un día lleno de cariño, salud y alegría. Gracias por estar siempre. 🎁❤️ Un abrazo fuerte. {firma}', 'Sí', 'Individual'], ['Año Nuevo', 1, 1, F, 'Todos', '🎆🥂 {querido} {nombre}: ¡feliz Año Nuevo! Te deseo un año lleno de salud, unión y felicidad junto a los tuyos. ✨❤️ Un abrazo. {firma}', 'Sí', 'Individual'], ['Día del Padre', 19, 3, F, 'Padres', '👨👧👦💙 {querido} {nombre}: ¡feliz Día del Padre! Gracias por tu cariño y tu ejemplo. Que tengas un día muy especial junto a los tuyos. 🎁🤗 {firma}', 'Sí', 'Individual'], ['Día de la Madre', 27, 5, F, 'Madres', '🌹💐 {querido} {nombre}: ¡feliz Día de la Madre! Gracias por tu cariño y por todo lo que haces. Que tengas un día muy especial junto a los tuyos. 🥰❤️ {firma}', 'Sí', 'Individual'], ['Navidad', 25, 12, F, 'Todos', '🎄🎁 {querido} {nombre}: ¡feliz Navidad! Que la pases en paz y junto a quienes más quieres. ❤️ Un abrazo. {firma}', 'Sí', 'Individual'] ]; } function crearFechas_(ss) { var h = ss.insertSheet(HOJA_FECHAS); var enc = ['Ocasión', 'Día', 'Mes', 'Relación', 'Aplica a (solo envío individual)', 'Mensaje (usa {estimado}, {querido}, {nombre}, {años} y {firma})', 'Activa (Sí/No)', 'Tipo de envío (Individual/Grupo)']; var anchos = [200, 50, 50, 110, 150, 620, 90, 140]; var datos = datosFechas_(); var n = ULTIMA_FILA_FECHAS - 1; h.getRange(1, 1, 1, enc.length).setValues([enc]); estiloEncabezado_(h.getRange(1, 1, 1, enc.length)); h.setRowHeight(1, 48); anchos.forEach(function (w, i) { h.setColumnWidth(i + 1, w); }); h.getRange(2, 1, n, enc.length).setFontFamily('Arial').setFontSize(10).setVerticalAlignment('top'); h.getRange(2, 1, datos.length, enc.length).setValues(datos); h.getRange(2, 6, n, 1).setWrap(true); h.getRange(2, 2, n, 3).setHorizontalAlignment('center'); h.getRange(2, 7, n, 2).setHorizontalAlignment('center'); h.getRange(2, 4, n, 1).setDataValidation(listaValidacion_(['Colaborador', 'Amigo', 'Familiar'])); h.getRange(2, 5, n, 1).setDataValidation(listaValidacion_(['Todos', 'Mujeres', 'Hombres', 'Madres', 'Padres', 'Según ficha'])); h.getRange(2, 7, n, 1).setDataValidation(listaValidacion_(['Sí', 'No'])); h.getRange(2, 8, n, 1).setDataValidation(listaValidacion_(['Individual', 'Grupo'])); h.getRange(2, 2, n, 1).setDataValidation(SpreadsheetApp.newDataValidation().requireNumberBetween(1, 31).setAllowInvalid(false).build()); h.getRange(2, 3, n, 1).setDataValidation(SpreadsheetApp.newDataValidation().requireNumberBetween(1, 12).setAllowInvalid(false).build()); h.setFrozenRows(1); } function crearConfig_(ss) { var h = ss.insertSheet(HOJA_CONFIG); var correo = ''; try { correo = Session.getEffectiveUser().getEmail(); } catch (e) { correo = ''; } h.setColumnWidth(1, 300); h.setColumnWidth(2, 260); h.setColumnWidth(3, 480); h.getRange('A1').setValue('Configuración del aviso diario').setFontWeight('bold').setFontSize(12); h.getRange('A2').setValue('Las celdas en azul son datos que puedes cambiar.').setFontStyle('italic'); h.getRange('A4:C4').setValues([['Parámetro', 'Valor', 'Nota']]); estiloEncabezado_(h.getRange('A4:C4')); h.getRange('A5:C7').setValues([ ['Correo que recibe el aviso', correo, 'Recibes un correo cada día solo si hay saludos pendientes.'], ['Hora del aviso (0 a 23)', 8, 'El activador diario se programa con esta hora, en la zona horaria de tu hoja.'], ['Tu nombre o firma', 'Tu nombre', 'Reemplaza {firma} al final de cada mensaje. Cámbiala por tu nombre.'] ]); h.getRange('B5:B7').setFontColor('#0000FF'); h.getRange('A5:C7').setWrap(true).setVerticalAlignment('top').setBorder(true, true, true, true, true, true, '#BFBFBF', SpreadsheetApp.BorderStyle.SOLID); h.getRange('A10').setValue('Cómo se usa').setFontWeight('bold'); h.getRange('A11:A16').setValues([ ['1. Registra a cada persona en la pestaña Contactos e indica su relación: Colaborador, Amigo o Familiar.'], ['2. En colaboradores, marca "Consentimiento = Sí" solo si la persona aceptó recibir saludos.'], ['3. Revisa y ajusta los textos en la pestaña Fechas especiales. Cada ocasión tiene un texto por relación.'], ['4. Cada día recibes un correo con el mensaje listo y un enlace que abre WhatsApp con el texto escrito.'], ['5. En los mensajes de tipo Grupo, WhatsApp te deja elegir el grupo; en los individuales, abre el chat de cada persona.'], ['6. Cada aviso queda anotado en la pestaña Registro de avisos.'] ]); h.getRange('A1:C16').setFontFamily('Arial'); } function crearRegistro_(ss) { var h = ss.insertSheet(HOJA_LOG); h.getRange('A1:D1').setValues([['Fecha y hora', 'Contacto', 'Ocasión', 'Resultado']]); estiloEncabezado_(h.getRange('A1:D1')); [150, 230, 220, 260].forEach(function (w, i) { h.setColumnWidth(i + 1, w); }); h.setRowHeight(1, 32); h.setFrozenRows(1); } /** Elimina la hoja inicial en blanco ("Hoja 1" o "Sheet1") si quedó vacía. */ function quitarHojaVacia_(ss) { ['Hoja 1', 'Hoja1', 'Sheet1'].forEach(function (nombre) { var h = ss.getSheetByName(nombre); if (h && ss.getSheets().length > 1 && h.getLastRow() === 0 && h.getLastColumn() === 0) ss.deleteSheet(h); }); } /* ------------------------------------------------------------------ */ /* AVISO DIARIO */ /* ------------------------------------------------------------------ */ /** Crea un activador diario a la hora indicada en Configuración (B6). */ function instalarActivador() { var hora = leerConfig_().hora; quitarActivador(); ScriptApp.newTrigger('enviarAvisoDiario') .timeBased() .atHour(hora) .everyDays(1) .inTimezone(TZ) .create(); SpreadsheetApp.getUi().alert('Aviso diario activado a las ' + hora + ':00 (zona horaria: ' + TZ + ').'); } function quitarActivador() { ScriptApp.getProjectTriggers().forEach(function (t) { if (t.getHandlerFunction() === 'enviarAvisoDiario') ScriptApp.deleteTrigger(t); }); } function enviarAvisoDiario() { var ss = SpreadsheetApp.getActiveSpreadsheet(); var cfg = leerConfig_(); var hoy = new Date(); var dia = Number(Utilities.formatDate(hoy, TZ, 'd')); var mes = Number(Utilities.formatDate(hoy, TZ, 'M')); var anio = Number(Utilities.formatDate(hoy, TZ, 'yyyy')); var contactos = ss.getSheetByName(HOJA_CONTACTOS).getDataRange().getValues(); var fechas = ss.getSheetByName(HOJA_FECHAS).getDataRange().getValues(); // Fechas especiales: A ocasión, B día, C mes, D relación, E aplica a, F mensaje, G activa, H tipo de envío. var plantillas = {}; // 'Relación|Cumpleaños' y 'Relación|Aniversario laboral' var generales = []; // fechas fijas de envío individual var avisos = []; for (var i = 1; i < fechas.length; i++) { var f = fechas[i]; var ocasion = String(f[0] || '').trim(); var relacion = String(f[3] || '').trim(); if (!ocasion || !relacion || String(f[6]).trim() !== 'Sí') continue; var tipo = String(f[7] || 'Individual').trim(); if (ocasion === 'Cumpleaños' || ocasion === 'Aniversario laboral') { plantillas[relacion + '|' + ocasion] = String(f[5]); } else if (Number(f[1]) === dia && Number(f[2]) === mes) { if (tipo === 'Grupo') { // Un solo mensaje para el grupo, sin depender de la lista de contactos. avisos.push(armarGrupo_(ocasion, relacion, String(f[5]), cfg.firma)); } else { generales.push({ ocasion: ocasion, relacion: relacion, audiencia: String(f[4]).trim(), texto: String(f[5]) }); } } } // Contactos: A nombre, B relación, C cargo/parentesco, D celular, E correo, F nacimiento, // G ingreso, H género, I madre/padre, J consentimiento, K activo. for (var r = 1; r < contactos.length; r++) { var c = contactos[r]; var nombreCompleto = String(c[0] || '').trim(); var rel = String(c[1] || '').trim(); if (!nombreCompleto || RELACIONES.indexOf(rel) < 0) continue; if (String(c[10]).trim() !== 'Sí') continue; // El consentimiento es obligatorio solo para colaboradores. if (rel === 'Colaborador' && String(c[9]).trim() !== 'Sí') continue; var tel = String(c[3] || '').replace(/\D/g, ''); if (tel.length < 8 || tel.length > 15) { registrar_(nombreCompleto, 'Celular no válido', 'Omitido: revisa el número'); continue; } var primerNombre = nombreCompleto.split(/\s+/)[0]; var genero = String(c[7]).trim().toUpperCase(); var padreMadre = String(c[8]).trim() === 'Sí'; var nac = c[5], ing = c[6]; var plCumple = plantillas[rel + '|Cumpleaños']; if (nac instanceof Date && coincide_(nac, dia, mes, anio) && plCumple) { avisos.push(armar_('Cumpleaños', rel, nombreCompleto, primerNombre, genero, tel, plCumple, '', cfg.firma)); } var plAnivers = plantillas[rel + '|Aniversario laboral']; if (rel === 'Colaborador' && ing instanceof Date && coincide_(ing, dia, mes, anio) && plAnivers) { var anios = anio - ing.getFullYear(); if (anios >= 1) { avisos.push(armar_('Aniversario laboral', rel, nombreCompleto, primerNombre, genero, tel, plAnivers, String(anios), cfg.firma)); } } generales.forEach(function (g) { if (g.relacion !== rel) return; var aplica = g.audiencia === 'Todos' || (g.audiencia === 'Mujeres' && genero === 'F') || (g.audiencia === 'Hombres' && genero === 'M') || (g.audiencia === 'Madres' && genero === 'F' && padreMadre) || (g.audiencia === 'Padres' && genero === 'M' && padreMadre); if (aplica) avisos.push(armar_(g.ocasion, rel, nombreCompleto, primerNombre, genero, tel, g.texto, '', cfg.firma)); }); } if (avisos.length === 0) return; enviarCorreo_(cfg.correo, avisos, hoy); avisos.forEach(function (a) { registrar_(a.nombre, a.ocasion, 'Aviso enviado por correo'); }); } /** Un 29 de febrero se saluda el 28 de febrero en años no bisiestos. */ function coincide_(fecha, dia, mes, anio) { var d = fecha.getDate(), m = fecha.getMonth() + 1; if (d === dia && m === mes) return true; var bisiesto = (anio % 4 === 0 && anio % 100 !== 0) || anio % 400 === 0; return d === 29 && m === 2 && !bisiesto && dia === 28 && mes === 2; } /** Estimada/Estimado y Querida/Querido según género; con género vacío, la forma (a). */ function tratamiento_(genero, f, m) { if (genero === 'F') return f; if (genero === 'M') return m; return m + '(a)'; } function armar_(ocasion, relacion, nombre, primerNombre, genero, tel, plantilla, anios, firma) { var texto = plantilla .replace(/\{estimado\}/g, tratamiento_(genero, 'Estimada', 'Estimado')) .replace(/\{querido\}/g, tratamiento_(genero, 'Querida', 'Querido')) .replace(/\{nombre\}/g, primerNombre) .replace(/\{años\}/g, anios) .replace(/\{firma\}/g, firma); return { ocasion: ocasion, relacion: relacion, nombre: nombre, texto: texto, enlace: 'https://wa.me/' + tel + '?text=' + encodeURIComponent(texto) }; } /** Mensaje genérico para un grupo: el enlace abre WhatsApp y tú eliges el grupo. */ function armarGrupo_(ocasion, relacion, plantilla, firma) { var texto = plantilla .replace(/\{firma\}/g, firma) .replace(/\{(estimado|querido|nombre|años)\}/g, ''); return { ocasion: ocasion, relacion: relacion, nombre: 'Grupo: ' + TITULOS[relacion], texto: texto, enlace: 'https://wa.me/?text=' + encodeURIComponent(texto) }; } function enviarCorreo_(destino, avisos, hoy) { var fecha = Utilities.formatDate(hoy, TZ, 'dd/MM/yyyy'); var html = '<p>Estimado(a):</p><p>Estos son los saludos de hoy (' + fecha + '). ' + 'Presiona el enlace para abrir WhatsApp con el mensaje listo y luego presiona Enviar. ' + 'En los mensajes para un grupo, WhatsApp te pedirá elegir el grupo.</p>'; RELACIONES.forEach(function (rel) { var items = avisos.filter(function (a) { return a.relacion === rel; }); if (items.length === 0) return; html += '<h3>' + esc_(TITULOS[rel]) + '</h3><ol>'; items.forEach(function (a) { html += '<li><b>' + esc_(a.nombre) + '</b> (' + esc_(a.ocasion) + ')<br>' + '<i>' + esc_(a.texto) + '</i><br>' + '<a href="' + a.enlace + '">Abrir WhatsApp</a></li><br>'; }); html += '</ol>'; }); html += '<p>Saludos cordiales.</p>'; MailApp.sendEmail({ to: destino, subject: 'Saludos de hoy (' + fecha + '): ' + avisos.length + ' pendiente(s)', htmlBody: html }); } function leerConfig_() { var h = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(HOJA_CONFIG); if (!h) throw new Error('Falta la pestaña Configuración. Usa el menú Saludos > 1. Instalar herramienta.'); var correo = String(h.getRange('B5').getValue()).trim(); var hora = Number(h.getRange('B6').getValue()); var firma = String(h.getRange('B7').getValue()).trim(); if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(correo)) throw new Error('Correo no válido en Configuración B5.'); if (!(hora >= 0 && hora <= 23) || hora % 1 !== 0) throw new Error('La hora en Configuración B6 debe ser un entero de 0 a 23.'); return { correo: correo, hora: hora, firma: firma }; } function registrar_(nombre, ocasion, resultado) { var h = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(HOJA_LOG); h.appendRow([Utilities.formatDate(new Date(), TZ, 'dd/MM/yyyy HH:mm'), nombre, ocasion, resultado]); } /** Escapa HTML para que ningún dato de la hoja altere el correo. */ function esc_(s) { return String(s).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"'); }Versión 1.0 · Actualizada en octubre de 2026. Es el mismo código para todos los usuarios.
-
Instala la herramienta
Vuelve a la hoja y recárgala con F5. Junto al menú Ayuda aparece un menú nuevo llamado Saludos (puede tardar unos segundos). Elige Saludos → 1. Instalar herramienta.
Google te pedirá autorización: es normal
- Presiona Revisar permisos y elige tu cuenta de Google.
- Si aparece "Google no ha verificado esta aplicación", presiona Configuración avanzada.
- Presiona Ir a Saludos (no seguro). El nombre es el que le diste al proyecto.
- Revisa los permisos y presiona Permitir.
Google lo marca así porque el código lo pegaste tú y no se publicó para el público. Los permisos son los mínimos: ver y editar esta hoja para crear las pestañas, enviarte el aviso por correo, y ejecutarse cada día a la hora que elijas.
Al terminar, la hoja muestra un mensaje de instalación completa y se crean cuatro pestañas: Contactos, Fechas especiales, Configuración y Registro de avisos. Si no ves ese mensaje después de autorizar, vuelve a elegir Saludos → 1. Instalar herramienta.
-
Configura tu correo y tu firma
Abre la pestaña Configuración. Las celdas en azul son las que puedes cambiar:
- Correo: ya viene con el de tu cuenta de Google. Ahí llegará el aviso diario.
- Hora del aviso: un número de 0 a 23. Por ejemplo, 8 es las 8:00 de la mañana.
- Firma: reemplaza "Tu nombre" por el nombre con el que quieres firmar los saludos.
-
Registra a tus contactos
Abre la pestaña Contactos y reemplaza la fila de ejemplo por tu primer contacto. Cada columna tiene listas desplegables o validaciones para evitar errores.
Columna Qué escribir Nombre completo El nombre de la persona. El saludo usa el primer nombre. Relación Elige de la lista: Colaborador, Amigo o Familiar. Celular Código de país y número, solo dígitos y sin signo +. En Bolivia: 591 y los 8 dígitos del celular (por ejemplo, 59170000000). Fecha de nacimiento En formato día/mes/año, por ejemplo 15/06/1990. Fecha de ingreso Solo para colaboradores. Se usa para el saludo de aniversario laboral. Género (F/M) Define el tratamiento: "Estimada" o "Estimado". Si queda vacío, se usa "Estimado(a)". Es madre o padre Sí o No. Se usa en los saludos individuales del Día de la Madre y del Día del Padre. Consentimiento Sí o No. En colaboradores es obligatorio: sin un "Sí" no se genera ningún saludo. Activo Sí para recibir avisos de esa persona. La fila de ejemplo viene en "No". Cargo o parentesco y correo Opcionales. Son datos de referencia; la herramienta no los usa por ahora. Si la hoja no acepta tus fechas: revisa la región de la hoja en Archivo → Configuración → Región y elige tu país, para que las fechas se lean como día/mes/año.
-
Activa el aviso diario y haz una prueba
Elige Saludos → 2. Activar aviso diario. Desde ese momento, cada día a la hora configurada recibirás un correo si hay saludos pendientes.
Para probar, agrega un contacto con la fecha de nacimiento de hoy (y "Activo = Sí") y elige Saludos → Enviar aviso de hoy ahora (prueba). Revisa tu bandeja de entrada y, si no aparece, la carpeta de spam.
Listo
Cuando llegue el correo, presiona el enlace de cada saludo: se abre WhatsApp con el mensaje escrito, solo falta enviarlo.
Personaliza los mensajes
Todo se edita en la pestaña Fechas especiales. Cada fila es una ocasión para una relación (por ejemplo, Navidad para Familiares).
Cambia el texto
Edita el mensaje en la columna correspondiente y usa los marcadores de la tabla de abajo.
Individual o grupo
Individual envía un mensaje por persona. Grupo genera un solo mensaje, y WhatsApp te deja elegir tu grupo.
Activa o agrega fechas
Pon "No" en Activa para silenciar una ocasión, o agrega una fila nueva con día, mes, relación y mensaje.
| Marcador | Se reemplaza por |
|---|---|
| {estimado} | Estimada o Estimado, según el género. |
| {querido} | Querida o Querido, según el género. Pensado para amigos y familiares. |
| {nombre} | El primer nombre del contacto. |
| {años} | Los años de servicio, en el aniversario laboral. |
| {firma} | El texto de la celda Firma en la pestaña Configuración. |
Preguntas frecuentes
¿Es realmente gratis?
Sí. Es de uso gratuito y no te pedimos registro ni correo para usarla.
¿CSEPI recibe mis datos o los de mis contactos?
No. La hoja y el código viven en tu cuenta de Google y los datos nunca pasan por esta web ni por CSEPI. El único correo que sale es el aviso diario que la herramienta te envía a ti mismo.
¿Por qué Google dice que la aplicación no está verificada?
Porque el código lo pegaste tú en tu propia hoja y no pasó por la revisión pública de Google, que solo se exige a aplicaciones que se publican para muchos usuarios. El script solo actúa dentro de tu cuenta. Puedes leer todo el código en esta página antes de instalarlo.
¿Los mensajes se envían solos?
No, y es a propósito. La herramienta te avisa y deja el mensaje listo; tú presionas Enviar en WhatsApp. WhatsApp restringe los envíos automáticos no oficiales y puede bloquear números que los usan, así que el envío manual es la forma más segura.
Uso WhatsApp Business, pero el enlace abre mi WhatsApp personal. ¿Qué hago?
El enlace no puede indicar con qué aplicación abrirse: el celular usa la que tiene asignada. En Android, entra a Ajustes → Aplicaciones → WhatsApp (la personal) → Abrir de forma predeterminada y desactiva la apertura de enlaces compatibles; haz lo contrario en WhatsApp Business. Los nombres exactos varían según la marca del celular. Otra salida es abrir el correo en una computadora con WhatsApp Web o de escritorio iniciado con tu cuenta Business, o copiar el mensaje del correo y pegarlo en el chat.
¿Cuántos contactos puedo registrar?
La hoja viene preparada con 300 filas. Puedes seguir agregando filas debajo: el script lee todas. Solo recuerda copiar hacia abajo la fila anterior para que las nuevas tengan listas desplegables y las columnas de próximo cumpleaños y años de servicio.
¿Cómo cambio la hora del aviso?
Cambia la hora en la pestaña Configuración (celda B6, de 0 a 23) y vuelve a elegir Saludos → 2. Activar aviso diario. Si el aviso llega a una hora distinta, revisa que la zona horaria de tu proyecto sea la de tu país: en el editor de Apps Script, entra a Configuración del proyecto.
¿Cómo desactivo el aviso?
Elige Saludos → Quitar aviso diario. Si quieres retirar por completo el permiso, puedes hacerlo desde la sección de seguridad de tu cuenta de Google, en las aplicaciones con acceso a tu cuenta.
¿Qué pasa si vuelvo a elegir "Instalar herramienta"?
No se pierde nada. Si una pestaña ya existe, la herramienta no la toca y solo crea las que falten.
Antes de usarla: consentimiento y privacidad
- Los celulares y las fechas de nacimiento son datos personales. Registra solo a personas que sepan que guardas sus datos y que hayan aceptado recibir tus saludos. Por eso la hoja exige el consentimiento en los colaboradores.
- Guarda la hoja en tu cuenta de Google y compártela solo con quien corresponda.
- Usa la herramienta para saludar, no para enviar mensajes masivos que nadie pidió.
- Cumple la normativa de protección de datos y de uso de WhatsApp que aplique en tu país.
- La herramienta se ofrece tal como está, sin garantía, y su uso es responsabilidad de quien la instala. Consulta los Términos y Condiciones y la Política de Privacidad de este sitio.
¿Te sirvió? Compártela con quien la necesite
¿Necesitas ayuda con tu contabilidad?
Escríbenos y conversemos sobre tus necesidades contables, tributarias o de automatización.