diff --git a/bot-admin-api/.env b/bot-admin-api/.env index e645ee2..bc7dc6e 100644 --- a/bot-admin-api/.env +++ b/bot-admin-api/.env @@ -1,4 +1,4 @@ -DATABASE_CONNECTION = mongodb://horacio:frx8us2w@192.168.1.50:20215/turnosxpress +DATABASE_CONNECTION = mongodb://horacio:frx8us2w@localhost:20215/turnosxpress DATABASE_CONNECTION_ATLAS = mongodb+srv://horacio:frx8us2w@reservar.a2cgd.mongodb.net/reservar DATABASE_CONNECTION_LOCAL = mongodb://horacio:frx8us2w@192.168.1.50:20215/turnosxpress diff --git a/notification-sender/src/Models/Jobs/JobProcessor.ts b/notification-sender/src/Models/Jobs/JobProcessor.ts index cba2984..c9a703d 100644 --- a/notification-sender/src/Models/Jobs/JobProcessor.ts +++ b/notification-sender/src/Models/Jobs/JobProcessor.ts @@ -8,8 +8,17 @@ const POLL_INTERVAL_MS = 5000; const BASE_RETRY_DELAY_MS = 5000; const MAX_JITTER_MS = 3000; const THROTTLE_RETRY_DELAY_MS = 8000; +const DEFAULT_CLEANUP_INTERVAL_MS = 60 * 60 * 1000; const SUPPORTED_APPOINTMENT_NOTIFICATION_TYPES = new Set(["creation", "reminder", "cancellation"]); +function resolveCleanupIntervalMs(): number { + const raw = process.env.NOTIFICATION_JOB_CLEANUP_INTERVAL_MS; + if (!raw) return DEFAULT_CLEANUP_INTERVAL_MS; + + const parsed = Number(raw); + return Number.isFinite(parsed) && parsed >= 0 ? parsed : DEFAULT_CLEANUP_INTERVAL_MS; +} + function resolveAppointmentNotificationType(type: unknown): string { return typeof type === "string" && SUPPORTED_APPOINTMENT_NOTIFICATION_TYPES.has(type) ? type : "reminder"; } @@ -28,6 +37,10 @@ export function calculateNextRetryAt(attempt: number, now: Date): Date { return new Date(now.getTime() + delay + jitter); } +export function buildStartOfDay(date: Date): Date { + return new Date(date.getFullYear(), date.getMonth(), date.getDate()); +} + function getJobId(job: { id?: unknown; _id?: unknown; appointmentId?: unknown; channel?: unknown; get?: (path: string) => unknown }): string { const value = job.id || job._id || job.get?.("_id"); if (value) return String(value); @@ -41,24 +54,53 @@ export class JobProcessor { private jobModel: NotificationJobModel; private throttle: WhatsAppThrottleClass; private polling = false; + private cleanupTimeout?: NodeJS.Timeout; + private cleanupIntervalMs: number; constructor() { this.jobModel = new NotificationJobModel(); this.throttle = new WhatsAppThrottleClass(); + this.cleanupIntervalMs = resolveCleanupIntervalMs(); } public async startPolling(): Promise { if (this.polling) return; this.polling = true; logIntent("JobProcessor: starting poll loop"); + await this.cleanupOldJobs(); + this.scheduleCleanup(); this.poll(); } public stopPolling(): void { this.polling = false; + if (this.cleanupTimeout) { + clearTimeout(this.cleanupTimeout); + this.cleanupTimeout = undefined; + } logIntent("JobProcessor: stopping poll loop"); } + private scheduleCleanup(): void { + if (!this.polling || this.cleanupIntervalMs <= 0) return; + + this.cleanupTimeout = setTimeout(async () => { + await this.cleanupOldJobs(); + this.scheduleCleanup(); + }, this.cleanupIntervalMs); + } + + private async cleanupOldJobs(now = new Date()): Promise { + const cutoff = buildStartOfDay(now); + + try { + const deletedCount = await this.jobModel.deleteBeforeScheduledAt(cutoff); + logDone(`JobProcessor: cleaned ${deletedCount} old notification jobs before ${cutoff.toISOString()}`); + } catch (error) { + logError(`JobProcessor: cleanup error before ${cutoff.toISOString()}`, error); + } + } + private async poll(): Promise { if (!this.polling) return; diff --git a/notification-sender/src/Models/Jobs/NotificationJob.Adapter.Mongoose.ts b/notification-sender/src/Models/Jobs/NotificationJob.Adapter.Mongoose.ts index 896640f..7f37b1e 100644 --- a/notification-sender/src/Models/Jobs/NotificationJob.Adapter.Mongoose.ts +++ b/notification-sender/src/Models/Jobs/NotificationJob.Adapter.Mongoose.ts @@ -102,4 +102,12 @@ export class NotificationJobModel { this.schema ); } + + async deleteBeforeScheduledAt(cutoff: Date): Promise { + const result = await this.notificationJobList.deleteMany({ + scheduledAt: { $lt: cutoff }, + }).exec(); + + return result.deletedCount || 0; + } } diff --git a/notification-sender/src/Models/Jobs/__tests__/JobProcessor.test.ts b/notification-sender/src/Models/Jobs/__tests__/JobProcessor.test.ts index 2c54994..5dde17c 100644 --- a/notification-sender/src/Models/Jobs/__tests__/JobProcessor.test.ts +++ b/notification-sender/src/Models/Jobs/__tests__/JobProcessor.test.ts @@ -1,5 +1,5 @@ import { dispatchEmail, dispatchWhatsApp } from "../ChannelDispatchers.js"; -import { buildPendingJobQuery, calculateNextRetryAt, JobProcessor } from "../JobProcessor.js"; +import { buildPendingJobQuery, buildStartOfDay, calculateNextRetryAt, JobProcessor } from "../JobProcessor.js"; import { resolveNotificationContent } from "../NotificationContentResolver.js"; jest.mock("../ChannelDispatchers.js", () => ({ @@ -94,6 +94,28 @@ describe("JobProcessor", () => { }); }); + describe("buildStartOfDay", () => { + it("returns the start of the local day", () => { + const result = buildStartOfDay(new Date(2026, 6, 21, 15, 30, 45, 123)); + + expect(result).toEqual(new Date(2026, 6, 21, 0, 0, 0, 0)); + }); + }); + + describe("old job cleanup", () => { + it("deletes jobs scheduled before the start of the current day", async () => { + const deleteBeforeScheduledAt = jest.fn().mockResolvedValue(3); + const now = new Date(2026, 6, 21, 15, 30, 0, 0); + + await (JobProcessor.prototype as any).cleanupOldJobs.call( + { jobModel: { deleteBeforeScheduledAt } }, + now + ); + + expect(deleteBeforeScheduledAt).toHaveBeenCalledWith(new Date(2026, 6, 21, 0, 0, 0, 0)); + }); + }); + describe("WhatsApp dispatch", () => { beforeEach(() => { jest.clearAllMocks(); diff --git a/txbot/package-lock.json b/txbot/package-lock.json index f127aed..b189ec7 100644 --- a/txbot/package-lock.json +++ b/txbot/package-lock.json @@ -9,7 +9,7 @@ "version": "1.0.0", "license": "ISC", "dependencies": { - "@whiskeysockets/baileys": "^6.7.18", + "@whiskeysockets/baileys": "6.17.16", "express": "^5.1.0", "qrcode": "^1.5.4" }, @@ -20,6 +20,12 @@ "typescript": "^5.8.3" } }, + "node_modules/@adiwajshing/keyed-db": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@adiwajshing/keyed-db/-/keyed-db-0.2.4.tgz", + "integrity": "sha512-yprSnAtj80/VKuDqRcFFLDYltoNV8tChNwFfIgcf6PGD4sjzWIBgs08pRuTqGH5mk5wgL6PBRSsMCZqtZwzFEw==", + "license": "MIT" + }, "node_modules/@cacheable/node-cache": { "version": "1.5.5", "resolved": "https://registry.npmjs.org/@cacheable/node-cache/-/node-cache-1.5.5.tgz", @@ -44,6 +50,12 @@ "node": ">=12" } }, + "node_modules/@eshaz/web-worker": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@eshaz/web-worker/-/web-worker-1.2.2.tgz", + "integrity": "sha512-WxXiHFmD9u/owrzempiDlBB1ZYqiLnm9s6aPc8AlFQalq2tKmqdmMr9GXOupDgzXtqnBipj8Un0gkIm7Sjf8mw==", + "license": "Apache-2.0" + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", @@ -445,6 +457,55 @@ "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==", "license": "BSD-3-Clause" }, + "node_modules/@thi.ng/bitstream": { + "version": "2.4.54", + "resolved": "https://registry.npmjs.org/@thi.ng/bitstream/-/bitstream-2.4.54.tgz", + "integrity": "sha512-uInkAJge5O0bWWEaYKrQpMccPbFg0z6eIA5NDCJXPm7l3rjlDje6RBHBXll3LiQz9Y051EdzlAEQRaB5hEifdg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/postspectacular" + }, + { + "type": "patreon", + "url": "https://patreon.com/thing_umbrella" + }, + { + "type": "liberapay", + "url": "https://liberapay.com/thi.ng" + } + ], + "license": "Apache-2.0", + "dependencies": { + "@thi.ng/errors": "^2.6.16" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@thi.ng/errors": { + "version": "2.6.16", + "resolved": "https://registry.npmjs.org/@thi.ng/errors/-/errors-2.6.16.tgz", + "integrity": "sha512-a7Lv/G0La5eTNUEIyLldpeYziyFSj3rOlWNeXFu8v+ZSb8w8EnQ/L0r0sKHux7Ru6RhUkAXrcaHU7xd1ZkWovA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/postspectacular" + }, + { + "type": "patreon", + "url": "https://patreon.com/thing_umbrella" + }, + { + "type": "liberapay", + "url": "https://liberapay.com/thi.ng" + } + ], + "license": "Apache-2.0", + "engines": { + "node": ">=18" + } + }, "node_modules/@tokenizer/token": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", @@ -835,44 +896,97 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/@whiskeysockets/baileys": { - "version": "6.7.18", - "resolved": "https://registry.npmjs.org/@whiskeysockets/baileys/-/baileys-6.7.18.tgz", - "integrity": "sha512-lstyxtAdOC7vuI9dWyxDg9DQlYSz8MhVsBkQUTX9EWt0GY0IJIa2Rnu4psGXa1OxgNvYwLaGSHiQBRil8mGb8g==", - "hasInstallScript": true, + "node_modules/@wasm-audio-decoders/common": { + "version": "9.0.7", + "resolved": "https://registry.npmjs.org/@wasm-audio-decoders/common/-/common-9.0.7.tgz", + "integrity": "sha512-WRaUuWSKV7pkttBygml/a6dIEpatq2nnZGFIoPTc5yPLkxL6Wk4YaslPM98OPQvWacvNZ+Py9xROGDtrFBDzag==", "license": "MIT", "dependencies": { + "@eshaz/web-worker": "1.2.2", + "simple-yenc": "^1.0.4" + } + }, + "node_modules/@wasm-audio-decoders/flac": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/@wasm-audio-decoders/flac/-/flac-0.2.10.tgz", + "integrity": "sha512-YfcyoD2rYRBa6ffawZKNi5qvV5HArJmNmuMVUPoutuZ2hhGi6WNSWIzgvbROGmPbFivLL764Am7xxJENWJDhjw==", + "license": "MIT", + "dependencies": { + "@wasm-audio-decoders/common": "9.0.7", + "codec-parser": "2.5.0" + }, + "funding": { + "type": "individual", + "url": "https://github.com/sponsors/eshaz" + } + }, + "node_modules/@wasm-audio-decoders/ogg-vorbis": { + "version": "0.1.20", + "resolved": "https://registry.npmjs.org/@wasm-audio-decoders/ogg-vorbis/-/ogg-vorbis-0.1.20.tgz", + "integrity": "sha512-zaQPasU5usRjUDXtXOHYED5tfkR4QMXd+EH3Nrz1+4+M5pCsdD+s9YxJqb0oqnTyRu/KUujOmu5Z/m/NT47vwg==", + "license": "MIT", + "dependencies": { + "@wasm-audio-decoders/common": "9.0.7", + "codec-parser": "2.5.0" + }, + "funding": { + "type": "individual", + "url": "https://github.com/sponsors/eshaz" + } + }, + "node_modules/@wasm-audio-decoders/opus-ml": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/@wasm-audio-decoders/opus-ml/-/opus-ml-0.0.2.tgz", + "integrity": "sha512-58rWEqDGg+CKCyEeKm2KoxxSwTWtHh/NLTW9ObR4K8CGF6VwuuGudEI1CtniS/oSRmL1nJq/eh8MKARiluw4DQ==", + "license": "MIT", + "dependencies": { + "@wasm-audio-decoders/common": "9.0.7" + }, + "funding": { + "type": "individual", + "url": "https://github.com/sponsors/eshaz" + } + }, + "node_modules/@whiskeysockets/baileys": { + "version": "6.17.16", + "resolved": "https://registry.npmjs.org/@whiskeysockets/baileys/-/baileys-6.17.16.tgz", + "integrity": "sha512-cZoUaKpO4fsDUNiCtyZfbjkW0Bjl/IudzHLCvpqfqtq5TACQzNynYsYdKPJz1I8Cu/SSEvmewk0RorIs0zDWyw==", + "deprecated": "This version is affected by a zero-day vulnerability that allows spoofing of messages, please update to\n the latest versions (6.7.22^ or 7.0.0-rc12^)! For more information, check out the public advisory at\n https://github.com/WhiskeySockets/Baileys/security/advisories/GHSA-qvv5-jq5g-4cgg", + "license": "MIT", + "dependencies": { + "@adiwajshing/keyed-db": "^0.2.4", "@cacheable/node-cache": "^1.4.0", "@hapi/boom": "^9.1.3", "@whiskeysockets/eslint-config": "github:whiskeysockets/eslint-config", - "async-mutex": "^0.5.0", + "async-lock": "^1.4.1", + "audio-decode": "^2.1.3", "axios": "^1.6.0", + "cache-manager": "^5.7.6", + "libphonenumber-js": "^1.10.20", "libsignal": "github:WhiskeySockets/libsignal-node", "lodash": "^4.17.21", "music-metadata": "^7.12.3", "pino": "^9.6", "protobufjs": "^7.2.4", + "uuid": "^10.0.0", "ws": "^8.13.0" }, - "engines": { - "node": ">=20.0.0" - }, "peerDependencies": { - "audio-decode": "^2.1.3", "jimp": "^0.16.1", "link-preview-js": "^3.0.0", + "qrcode-terminal": "^0.12.0", "sharp": "^0.32.6" }, "peerDependenciesMeta": { - "audio-decode": { - "optional": true - }, "jimp": { "optional": true }, "link-preview-js": { "optional": true }, + "qrcode-terminal": { + "optional": true + }, "sharp": { "optional": true } @@ -1006,14 +1120,11 @@ "license": "Python-2.0", "peer": true }, - "node_modules/async-mutex": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.5.0.tgz", - "integrity": "sha512-1A94B18jkJ3DYq284ohPxoXbfTA5HsQ7/Mf4DEhcyLx3Bz27Rh59iScbB6EPiP+B+joue6YCxcMXSbFC1tZKwA==", - "license": "MIT", - "dependencies": { - "tslib": "^2.4.0" - } + "node_modules/async-lock": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/async-lock/-/async-lock-1.4.1.tgz", + "integrity": "sha512-Az2ZTpuytrtqENulXwO3GGv1Bztugx6TT37NIo7imr/Qo0gsYiGtSdBa2B6fsXhTpVZDNfu1Qn3pk531e3q+nQ==", + "license": "MIT" }, "node_modules/asynckit": { "version": "0.4.0", @@ -1030,6 +1141,41 @@ "node": ">=8.0.0" } }, + "node_modules/audio-buffer": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/audio-buffer/-/audio-buffer-5.0.0.tgz", + "integrity": "sha512-gsDyj1wwUp8u7NBB+eW6yhLb9ICf+0eBmDX8NGaAS00w8/fLqFdxUlL5Ge/U8kB64DlQhdonxYC59dXy1J7H/w==", + "license": "MIT" + }, + "node_modules/audio-decode": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/audio-decode/-/audio-decode-2.2.3.tgz", + "integrity": "sha512-Z0lHvMayR/Pad9+O9ddzaBJE0DrhZkQlStrC1RwcAHF3AhQAsdwKHeLGK8fYKyp2DDU6xHxzGb4CLMui12yVrg==", + "deprecated": "Renamed to @audio/decode — same API; this name remains a thin alias. npm i @audio/decode", + "license": "MIT", + "dependencies": { + "@wasm-audio-decoders/flac": "^0.2.4", + "@wasm-audio-decoders/ogg-vorbis": "^0.1.15", + "audio-buffer": "^5.0.0", + "audio-type": "^2.2.1", + "mpg123-decoder": "^1.0.0", + "node-wav": "^0.0.2", + "ogg-opus-decoder": "^1.6.12", + "qoa-format": "^1.0.1" + } + }, + "node_modules/audio-type": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/audio-type/-/audio-type-2.4.2.tgz", + "integrity": "sha512-E9Bl1FGGDS81H/mKvaoPq0iQ8uT3qTBe6XWjipOo5kYrhLMAbjD7hREAVle0YJFahZOVbztxTdkwi0VBDJutSA==", + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/audiojs" + } + }, "node_modules/axios": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/axios/-/axios-1.9.0.tgz", @@ -1141,6 +1287,21 @@ "node": ">= 0.8" } }, + "node_modules/cache-manager": { + "version": "5.7.6", + "resolved": "https://registry.npmjs.org/cache-manager/-/cache-manager-5.7.6.tgz", + "integrity": "sha512-wBxnBHjDxF1RXpHCBD6HGvKER003Ts7IIm0CHpggliHzN1RZditb7rXoduE1rplc2DEFYKxhLKgFuchXMJje9w==", + "license": "MIT", + "dependencies": { + "eventemitter3": "^5.0.1", + "lodash.clonedeep": "^4.5.0", + "lru-cache": "^10.2.2", + "promise-coalesce": "^1.1.2" + }, + "engines": { + "node": ">= 18" + } + }, "node_modules/cacheable": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/cacheable/-/cacheable-1.9.0.tgz", @@ -1227,6 +1388,12 @@ "wrap-ansi": "^6.2.0" } }, + "node_modules/codec-parser": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/codec-parser/-/codec-parser-2.5.0.tgz", + "integrity": "sha512-Ru9t80fV8B0ZiixQl8xhMTLru+dzuis/KQld32/x5T/+3LwZb0/YvQdSKytX9JqCnRdiupvAvyYJINKrXieziQ==", + "license": "LGPL-3.0-or-later" + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -1803,6 +1970,12 @@ "node": ">=6" } }, + "node_modules/eventemitter3": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz", + "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==", + "license": "MIT" + }, "node_modules/events": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", @@ -2460,6 +2633,12 @@ "node": ">= 0.8.0" } }, + "node_modules/libphonenumber-js": { + "version": "1.13.9", + "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.13.9.tgz", + "integrity": "sha512-VNS5vWMM7r0P66BYv+TQJATxExEgLxN+34hfHDVhDkUsGAE4cRg0shCNSLTXNKm7nIUscC7AfB51TjxEeF7msQ==", + "license": "MIT" + }, "node_modules/libsignal": { "name": "@whiskeysockets/libsignal-node", "version": "2.0.1", @@ -2526,6 +2705,12 @@ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "license": "MIT" }, + "node_modules/lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==", + "license": "MIT" + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -2539,6 +2724,12 @@ "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", "license": "Apache-2.0" }, + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC" + }, "node_modules/make-error": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", @@ -2634,6 +2825,19 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/mpg123-decoder": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/mpg123-decoder/-/mpg123-decoder-1.0.3.tgz", + "integrity": "sha512-+fjxnWigodWJm3+4pndi+KUg9TBojgn31DPk85zEsim7C6s0X5Ztc/hQYdytXkwuGXH+aB0/aEkG40Emukv6oQ==", + "license": "MIT", + "dependencies": { + "@wasm-audio-decoders/common": "9.0.7" + }, + "funding": { + "type": "individual", + "url": "https://github.com/sponsors/eshaz" + } + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -2677,6 +2881,15 @@ "node": ">= 0.6" } }, + "node_modules/node-wav": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/node-wav/-/node-wav-0.0.2.tgz", + "integrity": "sha512-M6Rm/bbG6De/gKGxOpeOobx/dnGuP0dz40adqx38boqHhlWssBJZgLCPBNtb9NkrmnKYiV04xELq+R6PFOnoLA==", + "license": "MIT", + "engines": { + "node": ">=4.4.0" + } + }, "node_modules/object-inspect": { "version": "1.13.4", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", @@ -2689,6 +2902,22 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/ogg-opus-decoder": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/ogg-opus-decoder/-/ogg-opus-decoder-1.7.3.tgz", + "integrity": "sha512-w47tiZpkLgdkpa+34VzYD8mHUj8I9kfWVZa82mBbNwDvB1byfLXSSzW/HxA4fI3e9kVlICSpXGFwMLV1LPdjwg==", + "license": "MIT", + "dependencies": { + "@wasm-audio-decoders/common": "9.0.7", + "@wasm-audio-decoders/opus-ml": "0.0.2", + "codec-parser": "2.5.0", + "opus-decoder": "0.7.11" + }, + "funding": { + "type": "individual", + "url": "https://github.com/sponsors/eshaz" + } + }, "node_modules/on-exit-leak-free": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz", @@ -2737,6 +2966,19 @@ "node": ">= 0.8.0" } }, + "node_modules/opus-decoder": { + "version": "0.7.11", + "resolved": "https://registry.npmjs.org/opus-decoder/-/opus-decoder-0.7.11.tgz", + "integrity": "sha512-+e+Jz3vGQLxRTBHs8YJQPRPc1Tr+/aC6coV/DlZylriA29BdHQAYXhvNRKtjftof17OFng0+P4wsFIqQu3a48A==", + "license": "MIT", + "dependencies": { + "@wasm-audio-decoders/common": "9.0.7" + }, + "funding": { + "type": "individual", + "url": "https://github.com/sponsors/eshaz" + } + }, "node_modules/p-limit": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", @@ -2929,6 +3171,15 @@ ], "license": "MIT" }, + "node_modules/promise-coalesce": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/promise-coalesce/-/promise-coalesce-1.5.0.tgz", + "integrity": "sha512-cTJ30U+ur1LD7pMPyQxiKIwxjtAjLsyU7ivRhVWZrX9BNIXtf78pc37vSMc8Vikx7DVzEKNk2SEJ5KWUpSG2ig==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=16" + } + }, "node_modules/protobufjs": { "version": "7.4.0", "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.4.0.tgz", @@ -2982,6 +3233,15 @@ "node": ">=6" } }, + "node_modules/qoa-format": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/qoa-format/-/qoa-format-1.0.1.tgz", + "integrity": "sha512-dMB0Z6XQjdpz/Cw4Rf6RiBpQvUSPCfYlQMWvmuWlWkAT7nDQD29cVZ1SwDUB6DYJSitHENwbt90lqfI+7bvMcw==", + "license": "MIT", + "dependencies": { + "@thi.ng/bitstream": "^2.2.12" + } + }, "node_modules/qrcode": { "version": "1.5.4", "resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.5.4.tgz", @@ -3370,6 +3630,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/simple-yenc": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/simple-yenc/-/simple-yenc-1.0.4.tgz", + "integrity": "sha512-5gvxpSd79e9a3V4QDYUqnqxeD4HGlhCakVpb6gMnDD7lexJggSBJRBO5h52y/iJrdXRilX9UCuDaIJhSWm5OWw==", + "license": "MIT", + "funding": { + "type": "individual", + "url": "https://github.com/sponsors/eshaz" + } + }, "node_modules/sonic-boom": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.2.0.tgz", @@ -3578,12 +3848,6 @@ } } }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" - }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -3649,6 +3913,20 @@ "punycode": "^2.1.0" } }, + "node_modules/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", + "deprecated": "uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028).", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/v8-compile-cache-lib": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", diff --git a/txbot/package.json b/txbot/package.json index 0414c8c..751e69f 100644 --- a/txbot/package.json +++ b/txbot/package.json @@ -12,7 +12,7 @@ "license": "ISC", "description": "", "dependencies": { - "@whiskeysockets/baileys": "^6.7.18", + "@whiskeysockets/baileys": "6.17.16", "express": "^5.1.0", "qrcode": "^1.5.4" }, diff --git a/txbot/src/server.ts b/txbot/src/server.ts index 21c9bd0..0613473 100644 --- a/txbot/src/server.ts +++ b/txbot/src/server.ts @@ -65,7 +65,7 @@ async function startServer() { } try { const jid = number.endsWith("@s.whatsapp.net") ? number : `${number}@s.whatsapp.net`; - await sock.sendMessage(jid, { text: message, ephemeralExpiration: 0 }); + await sock.sendMessage(jid, { text: message }); res.json({ success: true }); } catch (err) { console.error("Send error:", err); diff --git a/txclient/src/app/admin/(organization-profile)/org/profile/[id]/notifications/page.tsx b/txclient/src/app/admin/(organization-profile)/org/profile/[id]/notifications/page.tsx index 7abcf95..048166a 100644 --- a/txclient/src/app/admin/(organization-profile)/org/profile/[id]/notifications/page.tsx +++ b/txclient/src/app/admin/(organization-profile)/org/profile/[id]/notifications/page.tsx @@ -25,9 +25,12 @@ const CHANNELS: { key: NotificationChannel; label: string }[] = [ ]; const REMINDER_PRESETS: { minutes: number; label: string }[] = [ - { minutes: 2, label: "2 minutos antes" }, - { minutes: 5, label: "5 minutos antes" }, - { minutes: 10, label: "10 minutos antes" } + { minutes: 60, label: "1 hora antes" }, + { minutes: 180, label: "3 horas antes" }, + { minutes: 360, label: "6 horas antes" }, + { minutes: 720, label: "12 horas antes" }, + { minutes: 1440, label: "1 día antes" }, + { minutes: 2880, label: "2 días antes" } ]; export default function NotificationsPage() {