Closer/functions/dist/couples/scheduledOutcomesReminder.js

123 lines
5.3 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.scheduledOutcomesReminder = void 0;
const admin = __importStar(require("firebase-admin"));
const scheduler_1 = require("firebase-functions/v2/scheduler");
const quietHours_1 = require("../notifications/quietHours");
const push_1 = require("../notifications/push");
const time_1 = require("../notifications/time");
const log_1 = require("../log");
const DAY_MS = 24 * 60 * 60 * 1000;
const REMINDER_DAYS = [30, 60, 90];
const DAY_KEY_MAP = { 30: 'day_30', 60: 'day_60', 90: 'day_90' };
exports.scheduledOutcomesReminder = (0, scheduler_1.onSchedule)({ schedule: 'every 24 hours', timeoutSeconds: 180 }, async () => {
var _a, _b;
const db = admin.firestore();
const messaging = admin.messaging();
const now = Date.now();
const couplesSnap = await db.collection('couples').limit(200).get();
const notifications = [];
for (const coupleDoc of couplesSnap.docs) {
const coupleId = coupleDoc.id;
const data = (_a = coupleDoc.data()) !== null && _a !== void 0 ? _a : {};
const createdAt = (0, time_1.toMillis)(data.createdAt);
if (createdAt <= 0)
continue;
const ageDays = Math.floor((now - createdAt) / DAY_MS);
const dueDays = REMINDER_DAYS.filter((day) => ageDays >= day && ageDays <= day + 2);
if (dueDays.length === 0)
continue;
const userIds = ((_b = data.userIds) !== null && _b !== void 0 ? _b : []);
if (userIds.length === 0)
continue;
// Check each due checkpoint; only remind for the first one without an outcome.
let remindedDay = null;
for (const day of dueDays) {
const dayKey = DAY_KEY_MAP[day];
const outcomeSnap = await coupleDoc.ref.collection('outcomes').doc(dayKey).get();
if (!outcomeSnap.exists) {
remindedDay = day;
break;
}
}
if (remindedDay == null)
continue;
const dayLabel = remindedDay;
const title = 'How are you feeling together?';
const body = `Youve been connected for ${dayLabel} days. Take a quick check-in to see how things have changed.`;
for (const userId of userIds) {
notifications.push({ userId, coupleId, day: dayLabel, title, body });
}
}
// Per-recipient failures are isolated so one bad send can't abort the batch.
await Promise.allSettled(notifications.map((notification) => sendOutcomeReminder(db, messaging, notification)));
log_1.logger.log(`[scheduledOutcomesReminder] scanned ${couplesSnap.size}; notified ${notifications.length}`);
});
async function sendOutcomeReminder(db, messaging, notification) {
const userDoc = await db.collection('users').doc(notification.userId).get();
const userData = userDoc.data();
// Honor the recipient's quiet hours (outcome check-ins are genuine, so no promotional gate).
if ((0, quietHours_1.recipientInQuietHours)(userData)) {
log_1.logger.log(`[sendOutcomeReminder] skip ${notification.userId} — quiet hours`);
return;
}
await db
.collection('users')
.doc(notification.userId)
.collection('notification_queue')
.add({
type: 'outcome_reminder',
title: notification.title,
body: notification.body,
coupleId: notification.coupleId,
day: notification.day,
read: false,
createdAt: admin.firestore.FieldValue.serverTimestamp(),
});
await (0, push_1.sendPushToUser)(db, messaging, notification.userId, {
notification: {
title: notification.title,
body: notification.body,
},
android: { notification: { channelId: 'reminders' } }, // E-OBS
data: {
type: 'outcome_reminder',
coupleId: notification.coupleId,
day: String(notification.day),
},
}, userData);
}
//# sourceMappingURL=scheduledOutcomesReminder.js.map