Ver instrucciones rápidas (30 seg) o ver el código completo
- Abre tu proyecto en
script.google.com y reemplaza el código por este bloque (busca automáticamente vuestro calendario "Casita Calendar").
- Pulsa Implementar → Gestionar implementaciones → Editar (✏️) → Versión: Nueva versión → Implementar.
- Pega la URL
.../exec arriba y pulsa 🔄 Sincronizar Ahora.
// Casita Calendar v3 — Sincronización Bidireccional en Tiempo Real (Padre ↔ Madre)
// Busca automáticamente el calendario compartido "Casita Calendar" y sincroniza eventos, listas y cambios.
var SHARED_TAG = '[CasitaApp:SharedState_v3]';
function getCasitaCalendar_() {
var byName = CalendarApp.getCalendarsByName('Casita Calendar');
if (byName && byName.length > 0) return byName[0];
var all = CalendarApp.getAllCalendars();
for (var i = 0; i < all.length; i++) {
if (all[i].getName().toLowerCase().indexOf('casita') !== -1) return all[i];
}
return CalendarApp.getDefaultCalendar();
}
function doGet(e) {
return handlePullAll_(e ? e.parameter : {});
}
function doPost(e) {
var body = {};
try {
body = JSON.parse(e.postData.contents);
} catch (err) {
return jsonOut_({ status: 'error', message: err.toString() });
}
if (body.action === 'PULL_ALL') {
return handlePullAll_(body);
}
var cal = getCasitaCalendar_();
// 1. Guardar estado compartido (Compras, Solicitudes de Cambio, Medicación, Turnos)
if (body.sharedState) {
saveSharedStateInCalendar_(cal, body.sharedState);
}
// 2. Crear o Actualizar Evento / Recordatorio en "Casita Calendar"
if (body.action === 'UPSERT_EVENT' || (body.summary && !body.targetService)) {
var evData = body.event || {};
var title = evData.summary || body.summary || 'Evento Casita';
var desc = evData.description || body.description || '';
var loc = evData.location || body.location || '';
var isAllDay = evData.start && evData.start.date && !evData.start.dateTime;
var existingEv = findEventByIdOrLocalTag_(cal, body.googleEventId, body.localId);
if (existingEv) {
existingEv.setTitle(title);
existingEv.setDescription(desc);
existingEv.setLocation(loc);
if (!isAllDay && (evData.start || body.startISO)) {
var s = new Date(evData.start ? evData.start.dateTime : body.startISO);
var en = new Date(evData.end ? evData.end.dateTime : (body.endISO || s.getTime() + 2700000));
existingEv.setTime(s, en);
}
return jsonOut_({ status: 'ok', googleEventId: existingEv.getId(), calendarName: cal.getName() });
} else {
var createdEv;
if (isAllDay) {
var dParts = evData.start.date.split('-');
var allDayDate = new Date(Number(dParts[0]), Number(dParts[1]) - 1, Number(dParts[2]));
createdEv = cal.createAllDayEvent(title, allDayDate, { description: desc, location: loc });
} else {
var start = new Date(evData.start ? evData.start.dateTime : (body.startISO || new Date()));
var end = new Date(evData.end ? evData.end.dateTime : (body.endISO || start.getTime() + 2700000));
createdEv = cal.createEvent(title, start, end, { description: desc, location: loc });
}
return jsonOut_({ status: 'ok', googleEventId: createdEv.getId(), calendarName: cal.getName() });
}
}
// 3. Eliminar Evento de "Casita Calendar"
if (body.action === 'DELETE_EVENT') {
var toDel = findEventByIdOrLocalTag_(cal, body.googleEventId, body.localId);
if (toDel) toDel.deleteEvent();
return jsonOut_({ status: 'ok', deleted: true });
}
// 4. Sincronizar también en Google Tasks si el servicio avanzado Tasks está activo
if (body.targetService === 'GOOGLE_TASKS' && typeof Tasks !== 'undefined') {
try {
var listId = getOrCreateCasitaTaskList_();
if (body.action === 'UPSERT_TASK' && body.task) {
var t = Tasks.Tasks.insert(body.task, listId);
return jsonOut_({ status: 'ok', googleTaskId: t.id });
}
} catch (err) {}
}
return jsonOut_({ status: 'ok', calendarName: cal.getName() });
}
function handlePullAll_(params) {
var cal = getCasitaCalendar_();
var now = new Date();
var tMin = params.timeMin ? new Date(params.timeMin) : new Date(now.getTime() - 14 * 86400000);
var tMax = params.timeMax ? new Date(params.timeMax) : new Date(now.getTime() + 60 * 86400000);
var rawEvents = cal.getEvents(tMin, tMax);
var events = [];
for (var i = 0; i < rawEvents.length; i++) {
var ev = rawEvents[i];
var isAllDay = ev.isAllDayEvent();
events.push({
id: ev.getId(),
summary: ev.getTitle(),
description: ev.getDescription() || '',
location: ev.getLocation() || '',
start: isAllDay
? { date: Utilities.formatDate(ev.getAllDayStartDate(), Session.getScriptTimeZone(), 'yyyy-MM-dd') }
: { dateTime: ev.getStartTime().toISOString() },
end: isAllDay
? { date: Utilities.formatDate(ev.getAllDayEndDate(), Session.getScriptTimeZone(), 'yyyy-MM-dd') }
: { dateTime: ev.getEndTime().toISOString() }
});
}
var sharedState = loadSharedStateFromCalendar_(cal);
var tasks = [];
if (typeof Tasks !== 'undefined') {
try {
var listId = getOrCreateCasitaTaskList_();
tasks = Tasks.Tasks.list(listId, { showCompleted: true }).items || [];
} catch (e) {}
}
return jsonOut_({
status: 'ok',
calendarName: cal.getName(),
events: events,
sharedState: sharedState,
tasks: tasks
});
}
function findEventByIdOrLocalTag_(cal, googleEventId, localId) {
if (googleEventId) {
try {
var byId = cal.getEventById(googleEventId);
if (byId) return byId;
} catch (e) {}
}
if (localId) {
var now = new Date();
var candidates = cal.getEvents(new Date(now.getTime() - 30 * 86400000), new Date(now.getTime() + 90 * 86400000), {
search: localId
});
if (candidates && candidates.length > 0) return candidates[0];
}
return null;
}
function saveSharedStateInCalendar_(cal, sharedStateObj) {
var d = new Date(2026, 0, 1);
var existing = cal.getEventsForDay(d, { search: 'Sincronización Casita Calendar' });
var desc = SHARED_TAG + '\n' + JSON.stringify(sharedStateObj);
if (existing && existing.length > 0) {
existing[0].setDescription(desc);
} else {
cal.createAllDayEvent('⚙️ [Sincronización Casita Calendar - No borrar]', d, { description: desc });
}
}
function loadSharedStateFromCalendar_(cal) {
try {
var d = new Date(2026, 0, 1);
var existing = cal.getEventsForDay(d, { search: 'Sincronización Casita Calendar' });
if (existing && existing.length > 0) {
var desc = existing[0].getDescription() || '';
if (desc.indexOf(SHARED_TAG) !== -1) {
return JSON.parse(desc.split(SHARED_TAG)[1].trim());
}
}
} catch (e) {}
return null;
}
function getOrCreateCasitaTaskList_() {
var lists = Tasks.Tasklists.list().items || [];
for (var i = 0; i < lists.length; i++) {
if (lists[i].title === 'Casita Calendar') return lists[i].id;
}
return Tasks.Tasklists.insert({ title: 'Casita Calendar' }).id;
}
function jsonOut_(obj) {
return ContentService.createTextOutput(JSON.stringify(obj))
.setMimeType(ContentService.MimeType.JSON);
}