@avakado.ai/schemas 1.1.20 → 1.1.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@avakado.ai/schemas",
3
- "version": "1.1.20",
3
+ "version": "1.1.22",
4
4
  "description": "Canonical Mongo schemas, models, enums and index declarations shared by the Avakado services.",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",
@@ -0,0 +1,74 @@
1
+ /** Session chat Meta allows after the lead's last inbound message. */
2
+ export const CHAT_SESSION_MS = 24 * 60 * 60 * 1000;
3
+ /** Callback window after a user-initiated WhatsApp call. */
4
+ export const WHATSAPP_CALL_CALLBACK_MS = 7 * 24 * 60 * 60 * 1000;
5
+
6
+ function windowUntil(mode, platform, at) {
7
+ const when = new Date(at);
8
+ if (mode === 'chat' && platform === 'telegram') return null;
9
+ if (mode === 'call' && platform === 'phone') return null;
10
+ if (mode === 'call' && platform === 'whatsapp') return new Date(when.getTime() + WHATSAPP_CALL_CALLBACK_MS);
11
+ return new Date(when.getTime() + CHAT_SESSION_MS);
12
+ }
13
+
14
+ export function contactWindowValue(mode, platform, at) {
15
+ return { allowed: true, validUntil: windowUntil(mode, platform, at) };
16
+ }
17
+
18
+ /** Replace the matched handle's window. Use this when the user grants or refuses permission. */
19
+ export function setContactWindow(update, mode, platform, { allowed, validUntil = null }) {
20
+ const prefix = `contactDetails.${platform}.$.${mode}`;
21
+ update.$set ??= {};
22
+ update.$set[`${prefix}.allowed`] = Boolean(allowed);
23
+ update.$set[`${prefix}.validUntil`] = validUntil;
24
+ return update;
25
+ }
26
+
27
+ /** Merge a window refresh onto the matched contactDetails entry. The query must match that handle. A later inbound only extends validUntil. */
28
+ export function applyContactWindow(update, mode, platform, at) {
29
+ const prefix = `contactDetails.${platform}.$.${mode}`;
30
+ const until = windowUntil(mode, platform, at);
31
+ update.$set ??= {};
32
+ update.$set[`${prefix}.allowed`] = true;
33
+ if (!until) {
34
+ update.$set[`${prefix}.validUntil`] = null;
35
+ return update;
36
+ }
37
+ update.$max ??= {};
38
+ update.$max[`${prefix}.validUntil`] = until;
39
+ return update;
40
+ }
41
+
42
+ export function contactWindowIsOpen(window, at = new Date()) {
43
+ if (!window?.allowed) return false;
44
+ if (!window.validUntil) return true;
45
+ return new Date(window.validUntil).getTime() > new Date(at).getTime();
46
+ }
47
+
48
+ function recordedWindow(window) {
49
+ if (!window || (window.allowed == null && window.validUntil == null)) return null;
50
+ return window;
51
+ }
52
+
53
+ /** The window on the primary handle, or the one that stays open longest. */
54
+ export function storedContactWindow(lead, mode, platform) {
55
+ const entries = lead?.contactDetails?.[platform];
56
+ if (!Array.isArray(entries)) return null;
57
+ const primary = recordedWindow(entries.find((entry) => entry.isPrimary)?.[mode]);
58
+ if (primary) return primary;
59
+ return entries.reduce((best, entry) => {
60
+ const window = recordedWindow(entry?.[mode]);
61
+ if (!window) return best;
62
+ if (!best) return window;
63
+ if (!window.validUntil) return window;
64
+ if (!best.validUntil) return best;
65
+ return new Date(window.validUntil) > new Date(best.validUntil) ? window : best;
66
+ }, null);
67
+ }
68
+
69
+ /** True only when a stored window exists and is closed. A missing window is left to the caller. */
70
+ export function sessionChatClosed(lead, platform, at = new Date()) {
71
+ const window = storedContactWindow(lead, 'chat', platform);
72
+ if (!window) return false;
73
+ return !contactWindowIsOpen(window, at);
74
+ }
package/src/index.js CHANGED
@@ -3,3 +3,13 @@ export * from "./models/index.js";
3
3
  export * from "./enums/index.js";
4
4
  export * from "./media.js";
5
5
  export { configureSchemas, defineModel, getSchemaConfig } from "./runtime.js";
6
+ export {
7
+ CHAT_SESSION_MS,
8
+ WHATSAPP_CALL_CALLBACK_MS,
9
+ applyContactWindow,
10
+ contactWindowIsOpen,
11
+ contactWindowValue,
12
+ sessionChatClosed,
13
+ setContactWindow,
14
+ storedContactWindow,
15
+ } from "./contactWindows.js";
@@ -2,12 +2,18 @@ import { Schema } from "mongoose";
2
2
  import { defineModel } from "../runtime.js";
3
3
  import { LEAD_STATUS } from "../enums/index.js";
4
4
  import { attachStatusChangeTrigger } from "../statusTrigger.js";
5
+ const contactWindowSchema = new Schema({
6
+ allowed: Boolean,
7
+ validUntil: Date,
8
+ }, { _id: false });
5
9
  const platformEntrySchema = new Schema({
6
10
  platform: { type: String, required: true }, // Phone,'Twitter', 'Instagram', 'Whatsapp', etc.
7
11
  handle: { type: String, trim: true },
8
12
  label: { type: String, trim: true }, // 'work', 'personal', 'brand'
9
13
  isPrimary: { type: Boolean, default: false },
10
- metadata: { type: Schema.Types.Mixed, default: {} }
14
+ metadata: { type: Schema.Types.Mixed, default: {} },
15
+ chat: contactWindowSchema,
16
+ call: contactWindowSchema,
11
17
  }, { _id: false });
12
18
  const contactDetailsSchema = new Schema({
13
19
  whatsapp: [platformEntrySchema],
@@ -1,8 +1,8 @@
1
1
  import { Schema } from "mongoose";
2
2
  import { defineModel } from "../runtime.js";
3
-
3
+ import { WORKFLOW_TRIGGERS } from "../enums/index.js";
4
4
  const TriggerTemplateSchema = new Schema({
5
- name: { type: String, required: true, unique: true, index: true }, // convers
5
+ name: { type: String, required: true, unique: true, index: true, enum: WORKFLOW_TRIGGERS },
6
6
  description: String,
7
7
  type: String,
8
8
  payload: Schema.Types.Mixed,
package/types.d.ts CHANGED
@@ -304,7 +304,7 @@ export interface Lead {
304
304
  business: Types.ObjectId;
305
305
  createdBy?: Types.ObjectId;
306
306
  name?: string;
307
- contactDetails?: { whatsapp?: { platform: string; handle?: string; label?: string; isPrimary?: boolean; metadata?: any; }[]; telegram?: { platform: string; handle?: string; label?: string; isPrimary?: boolean; metadata?: any; }[]; email?: { platform: string; handle?: string; label?: string; isPrimary?: boolean; metadata?: any; }[]; phone?: { platform: string; handle?: string; label?: string; isPrimary?: boolean; metadata?: any; }[]; twitter?: { platform: string; handle?: string; label?: string; isPrimary?: boolean; metadata?: any; }[]; instagram?: { platform: string; handle?: string; label?: string; isPrimary?: boolean; metadata?: any; }[]; facebook?: { platform: string; handle?: string; label?: string; isPrimary?: boolean; metadata?: any; }[]; };
307
+ contactDetails?: { whatsapp?: { platform: string; handle?: string; label?: string; isPrimary?: boolean; metadata?: any; chat?: { allowed?: boolean; validUntil?: Date; }; call?: { allowed?: boolean; validUntil?: Date; }; }[]; telegram?: { platform: string; handle?: string; label?: string; isPrimary?: boolean; metadata?: any; chat?: { allowed?: boolean; validUntil?: Date; }; call?: { allowed?: boolean; validUntil?: Date; }; }[]; email?: { platform: string; handle?: string; label?: string; isPrimary?: boolean; metadata?: any; chat?: { allowed?: boolean; validUntil?: Date; }; call?: { allowed?: boolean; validUntil?: Date; }; }[]; phone?: { platform: string; handle?: string; label?: string; isPrimary?: boolean; metadata?: any; chat?: { allowed?: boolean; validUntil?: Date; }; call?: { allowed?: boolean; validUntil?: Date; }; }[]; twitter?: { platform: string; handle?: string; label?: string; isPrimary?: boolean; metadata?: any; chat?: { allowed?: boolean; validUntil?: Date; }; call?: { allowed?: boolean; validUntil?: Date; }; }[]; instagram?: { platform: string; handle?: string; label?: string; isPrimary?: boolean; metadata?: any; chat?: { allowed?: boolean; validUntil?: Date; }; call?: { allowed?: boolean; validUntil?: Date; }; }[]; facebook?: { platform: string; handle?: string; label?: string; isPrimary?: boolean; metadata?: any; chat?: { allowed?: boolean; validUntil?: Date; }; call?: { allowed?: boolean; validUntil?: Date; }; }[]; };
308
308
  source?: string;
309
309
  tags?: string[];
310
310
  leadScore?: number;