@talkpilot/core-db 1.3.88 → 1.3.89
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/README.md +65 -12
- package/dist/talkpilot/auditLogs/auditLogs.getters.d.ts +8 -0
- package/dist/talkpilot/auditLogs/auditLogs.getters.d.ts.map +1 -0
- package/dist/talkpilot/auditLogs/auditLogs.getters.js +26 -0
- package/dist/talkpilot/auditLogs/auditLogs.getters.js.map +1 -0
- package/dist/talkpilot/auditLogs/auditLogs.types.d.ts +11 -0
- package/dist/talkpilot/auditLogs/auditLogs.types.d.ts.map +1 -0
- package/dist/talkpilot/auditLogs/auditLogs.types.js +3 -0
- package/dist/talkpilot/auditLogs/auditLogs.types.js.map +1 -0
- package/dist/talkpilot/auditLogs/index.d.ts +3 -0
- package/dist/talkpilot/auditLogs/index.d.ts.map +1 -0
- package/dist/talkpilot/auditLogs/index.js +19 -0
- package/dist/talkpilot/auditLogs/index.js.map +1 -0
- package/dist/talkpilot/calls/calls.constants.d.ts +11 -1
- package/dist/talkpilot/calls/calls.constants.d.ts.map +1 -1
- package/dist/talkpilot/calls/calls.constants.js +11 -1
- package/dist/talkpilot/calls/calls.constants.js.map +1 -1
- package/dist/talkpilot/calls/calls.getters.d.ts +8 -1
- package/dist/talkpilot/calls/calls.getters.d.ts.map +1 -1
- package/dist/talkpilot/calls/calls.getters.js +216 -1
- package/dist/talkpilot/calls/calls.getters.js.map +1 -1
- package/dist/talkpilot/calls/calls.types.d.ts +45 -0
- package/dist/talkpilot/calls/calls.types.d.ts.map +1 -1
- package/dist/talkpilot/clients/clients.constants.d.ts +4 -0
- package/dist/talkpilot/clients/clients.constants.d.ts.map +1 -0
- package/dist/talkpilot/clients/clients.constants.js +7 -0
- package/dist/talkpilot/clients/clients.constants.js.map +1 -0
- package/dist/talkpilot/clients/clients.getters.d.ts +6 -0
- package/dist/talkpilot/clients/clients.getters.d.ts.map +1 -1
- package/dist/talkpilot/clients/clients.getters.js +62 -1
- package/dist/talkpilot/clients/clients.getters.js.map +1 -1
- package/dist/talkpilot/clients/clients.lifecycle.d.ts +10 -0
- package/dist/talkpilot/clients/clients.lifecycle.d.ts.map +1 -0
- package/dist/talkpilot/clients/clients.lifecycle.js +109 -0
- package/dist/talkpilot/clients/clients.lifecycle.js.map +1 -0
- package/dist/talkpilot/clients/clients.types.d.ts +53 -0
- package/dist/talkpilot/clients/clients.types.d.ts.map +1 -1
- package/dist/talkpilot/clients/index.d.ts +2 -0
- package/dist/talkpilot/clients/index.d.ts.map +1 -1
- package/dist/talkpilot/clients/index.js +2 -0
- package/dist/talkpilot/clients/index.js.map +1 -1
- package/dist/talkpilot/contextNotes/contextNotes.getters.d.ts +1 -7
- package/dist/talkpilot/contextNotes/contextNotes.getters.d.ts.map +1 -1
- package/dist/talkpilot/contextNotes/contextNotes.getters.js +1 -37
- package/dist/talkpilot/contextNotes/contextNotes.getters.js.map +1 -1
- package/dist/talkpilot/contextNotes/contextNotes.types.d.ts +0 -6
- package/dist/talkpilot/contextNotes/contextNotes.types.d.ts.map +1 -1
- package/dist/talkpilot/index.d.ts +1 -0
- package/dist/talkpilot/index.d.ts.map +1 -1
- package/dist/talkpilot/index.js +1 -0
- package/dist/talkpilot/index.js.map +1 -1
- package/dist/talkpilot/phone_numbers/phone_numbers.getter.d.ts +9 -1
- package/dist/talkpilot/phone_numbers/phone_numbers.getter.d.ts.map +1 -1
- package/dist/talkpilot/phone_numbers/phone_numbers.getter.js +70 -1
- package/dist/talkpilot/phone_numbers/phone_numbers.getter.js.map +1 -1
- package/dist/talkpilot/phone_numbers/phone_numbers.types.d.ts +5 -0
- package/dist/talkpilot/phone_numbers/phone_numbers.types.d.ts.map +1 -1
- package/dist/test-utils/factories/index.d.ts +2 -0
- package/dist/test-utils/factories/index.d.ts.map +1 -1
- package/dist/test-utils/factories/index.js +2 -0
- package/dist/test-utils/factories/index.js.map +1 -1
- package/dist/test-utils/factories/talkpilot/clients.d.ts +14 -0
- package/dist/test-utils/factories/talkpilot/clients.d.ts.map +1 -0
- package/dist/test-utils/factories/talkpilot/clients.js +19 -0
- package/dist/test-utils/factories/talkpilot/clients.js.map +1 -0
- package/dist/test-utils/factories/talkpilot/results.d.ts +16 -0
- package/dist/test-utils/factories/talkpilot/results.d.ts.map +1 -0
- package/dist/test-utils/factories/talkpilot/results.js +40 -0
- package/dist/test-utils/factories/talkpilot/results.js.map +1 -0
- package/dist/utils/regex.utils.d.ts +2 -0
- package/dist/utils/regex.utils.d.ts.map +1 -0
- package/dist/utils/regex.utils.js +6 -0
- package/dist/utils/regex.utils.js.map +1 -0
- package/dist/websitalk/scanResources/scanResources.getters.d.ts.map +1 -1
- package/dist/websitalk/scanResources/scanResources.getters.js +2 -1
- package/dist/websitalk/scanResources/scanResources.getters.js.map +1 -1
- package/dist/websitalk/scanResources/scanResources.utils.d.ts +1 -1
- package/dist/websitalk/scanResources/scanResources.utils.d.ts.map +1 -1
- package/dist/websitalk/scanResources/scanResources.utils.js +3 -3
- package/dist/websitalk/scanResources/scanResources.utils.js.map +1 -1
- package/package.json +1 -1
- package/src/talkpilot/auditLogs/__tests__/auditLogs.spec.ts +91 -0
- package/src/talkpilot/auditLogs/auditLogs.getters.ts +26 -0
- package/src/talkpilot/auditLogs/auditLogs.types.ts +12 -0
- package/src/talkpilot/auditLogs/index.ts +2 -0
- package/src/talkpilot/calls/__tests__/calls.statistics.getters.spec.ts +559 -0
- package/src/talkpilot/calls/calls.constants.ts +14 -0
- package/src/talkpilot/calls/calls.getters.ts +320 -0
- package/src/talkpilot/calls/calls.types.ts +52 -0
- package/src/talkpilot/clients/__tests__/clients.getters.spec.ts +224 -0
- package/src/talkpilot/clients/__tests__/clients.lifecycle.spec.ts +194 -0
- package/src/talkpilot/clients/clients.constants.ts +4 -0
- package/src/talkpilot/clients/clients.getters.ts +91 -2
- package/src/talkpilot/clients/clients.lifecycle.ts +132 -0
- package/src/talkpilot/clients/clients.types.ts +58 -0
- package/src/talkpilot/clients/index.ts +2 -0
- package/src/talkpilot/contextNotes/__tests__/contextNotes.getters.spec.ts +0 -141
- package/src/talkpilot/contextNotes/contextNotes.getters.ts +1 -75
- package/src/talkpilot/contextNotes/contextNotes.types.ts +0 -7
- package/src/talkpilot/index.ts +1 -0
- package/src/talkpilot/phone_numbers/__tests__/phone_numbers.spec.ts +284 -13
- package/src/talkpilot/phone_numbers/phone_numbers.getter.ts +89 -1
- package/src/talkpilot/phone_numbers/phone_numbers.types.ts +6 -0
- package/src/test-utils/factories/index.ts +2 -0
- package/src/test-utils/factories/talkpilot/clients.ts +21 -0
- package/src/test-utils/factories/talkpilot/results.ts +43 -0
- package/src/utils/regex.utils.ts +2 -0
- package/src/websitalk/scanResources/scanResources.getters.ts +2 -6
- package/src/websitalk/scanResources/scanResources.utils.ts +2 -3
- package/dist/configuration/gptLive.seed.d.ts +0 -2
- package/dist/configuration/gptLive.seed.d.ts.map +0 -1
- package/dist/configuration/gptLive.seed.js +0 -59
- package/dist/configuration/gptLive.seed.js.map +0 -1
- package/dist/talkpilot/clients/clients.quota.constants.d.ts +0 -2
- package/dist/talkpilot/clients/clients.quota.constants.d.ts.map +0 -1
- package/dist/talkpilot/clients/clients.quota.constants.js +0 -5
- package/dist/talkpilot/clients/clients.quota.constants.js.map +0 -1
- package/dist/talkpilot/clients/clients.quota.utils.d.ts +0 -8
- package/dist/talkpilot/clients/clients.quota.utils.d.ts.map +0 -1
- package/dist/talkpilot/clients/clients.quota.utils.js +0 -45
- package/dist/talkpilot/clients/clients.quota.utils.js.map +0 -1
package/README.md
CHANGED
|
@@ -37,6 +37,7 @@ L1:# @talkpilot/core-db
|
|
|
37
37
|
- **TalkPilot domain** – Imports like `findAgents`, `getFlows`, `findCalls`, and `vectorSearchCalls` live in `src/talkpilot`. These functions are the canonical access pattern for call history, session metadata, and provider configs.
|
|
38
38
|
- **Municipal domain** – Helpers such as `findStreets`, `getMunicipalCities`, `findDepartmentSubjects`, and `createTicket` live under `src/municipal` and feed MIS workflows (street hints, subject matching, Ash Bina tickets).
|
|
39
39
|
- **Configuration domain** – `getPromptByName`, `findPrompts`, `getAllPrompts`, and `getPromptsByProduct` live under `src/configuration` and back MIS's `moked_106` prompt lookups (defaults, overridable per-client via `clientConfig.toolsPrompts`).
|
|
40
|
+
- **Websitalk domain** (`scans`, `websiteUrls`) – ⚠️ **Not included in `clientExistsInAnyCollection`/`deleteClientDataAcrossDomains`.** Those collections hold the client's URLs, which an external website-talk API uses as a lookup key to clean up its own Mongo collections *and* other, non-Mongo data stores tied to the client. Deleting a client fully requires calling that external API separately — see the warning on `deleteClientDataAcrossDomains` in `src/talkpilot/clients/clients.lifecycle.ts`.
|
|
40
41
|
|
|
41
42
|
## Environment variables
|
|
42
43
|
|
|
@@ -178,17 +179,18 @@ Refer to `DEVELOPMENT.md` for the full walkthrough, token instructions, and fact
|
|
|
178
179
|
| 1.3.47 | Added optional `accountOwnerEmail` field to `WebsiteTalkProduct` (CP-1715): the recipient for the end-of-call summary email, set once from the client's login email and editable in Settings. |
|
|
179
180
|
| 1.3.48 | Fixed `getScanPages` search (CP-1726): user search input was passed unescaped into MongoDB's `$regex`, so a URL/title containing regex-special characters (e.g. `?`) failed to match even when the document existed. Added `escapeRegex()` and applied it before building the `url`/`title` `$regex` filter. No API changes. |
|
|
180
181
|
| 1.3.49 | Internal tools configuration on flows (CP-1723): `Flow.internalTools` keyed by tool name (`enabled`, optional `maxCalls`, per-parameter `description`/`value` overrides) with matching `$jsonSchema` validator, plus `createFlow` / `updateFlowById` mutators and the `FlowUpdateParams` type so consumers stop writing to the `flows` collection directly. |
|
|
182
|
+
| 1.3.52 | Client-lifecycle support for the TalkPilot Dashboard (PE-869): `clientExistsInAnyCollection`/`deleteClientDataAcrossDomains` (dynamic scan across talkpilot/municipal domains only, no hardcoded collection list; `auditLogs` is excluded so a client's own deletion record survives its deletion), new `auditLogs` collection & `insertAuditLog`/`findAuditLogs`/`countAuditLogs`, `getAllClientCredits` on `clients`. Added `results` statistics getters for the dashboard: `aggregateActiveCallsSummary`, `aggregateTodayCallsSummary`, `aggregateClientCallStats`, `aggregateSessionCallStats`, and `findClientIdsByCallSids` (for backOffice-server). On `phone_numbers`: fixed `getClientPhones`/`getAllClientPhoneMappings` to deduplicate append-only history records instead of returning raw duplicate rows per client; added `getDistinctClientPhonePairs`, `findClientsByPhoneNumbers`, and `deletePhoneNumberRecords` (removes every record for a phone number, across all clients, once it's released back to Twilio). website-talk (`scans`/`websiteUrls`) is intentionally NOT touched by `deleteClientDataAcrossDomains` — see the Websitalk domain note above; callers must call the external website-talk deletion API separately. No breaking changes. |
|
|
181
183
|
| 1.3.57 | Phone-number bind getters: list by client, exclusive flow assign, unbind, and `flow_id` match for ObjectId or leftover string. `getClientByClientId`. |
|
|
182
184
|
| 1.3.61 | Optional `activeFlowId` on `WebsiteTalkProduct` (CP-1849). WebsiteTalk updates this flow instead of the flow on a phone number. |
|
|
183
185
|
| 1.3.62 | Pause/Resume support for WebsiteTalk scans (CP-1752): `PAUSED` and `CANCELED` added to `SCAN_STATUSES` (`PAUSED` deliberately excluded from `FINISHED_SCAN_STATUSES` — pausing isn't done, `CANCELED` is). Added `Scan.pausedAt`, tracked across pause/resume so elapsed-time display can freeze correctly instead of counting the paused duration. |
|
|
184
186
|
| 1.3.64 | New `callTokenUsage` collection with `incrementCallTokenUsage` (CP-1810): per-call realtime token usage, one document per `callSid`. |
|
|
185
187
|
| 1.3.65 | `Flow.beforeCallEvent` as an optional HTTP config object (`url` required; `timeoutMs`, `sendUserAuthToken`, `allowedKeys`, `wait`). `afterCallEvent` remains an optional Pub/Sub string. |
|
|
188
|
+
| 1.3.66 | Added `calls`-based stats getters for the dashboard: `aggregateActiveCallsSummary`, `aggregateTodayCallsSummary`, `aggregateClientCallStats`, `aggregateSessionCallStats`, `getAllClientsLastCallDate`, `findClientIdsByCallSids` — read the flat `calls` collection directly instead of unwinding `results.calls[]`, with a full per-status breakdown (`ALL_STATUSES_FOR_BREAKDOWN`) instead of a fixed 5-status set. **Removed** the 1.3.52 `results`-based getters of the same names — they were never released, so this isn't a breaking change for any consumer. |
|
|
186
189
|
| 1.3.69 | Municipal `residents` collection: optional resident fields (`cityName`, `firstName`, `lastName`, `idNumber`, `phoneNumber`, `address`) with exact-match getters by phone, by idNumber, and by phone+idNumber (city-scoped). Schema + indexes exported; collection is created out of band. |
|
|
187
190
|
| 1.3.71 | Who ended a call (CP-1873): optional `Call.endedBy` (`system` / `agent` / `user`), `CallEndedByValues`, and `setCallEndedBy` (first write wins). **Breaking** vs 1.3.69–1.3.70: the value `resident` was replaced by `user`. |
|
|
188
191
|
| 1.3.74 | Added `human` to `CallEndedByValues` (CP-1873): a human agent ended the call, after a transfer or in a conference. |
|
|
189
192
|
| 1.3.82 | Complaint tracking on `muniIssues` (CP-1888): optional `status` / `response` / `updatedAt`; an editable TalkPilot response with up to `MUNI_ISSUE_MAX_RESPONSE_IMAGES` (5) `response.imageUrls` and `response.updatedAt`; `updateMuniIssueStatus`, `setMuniIssueResponse`, `findMuniIssuesByClient`, `countMuniIssuesByClient`, `getMuniIssueByIdForClient`; index `clientId_1_createdAt_-1`. |
|
|
190
|
-
| 1.3.
|
|
191
|
-
|
|
193
|
+
| 1.3.89 | Backoffice support: roles (`BACKOFFICE_ROLES`, `getBackofficeRole`, `setBackofficeRole`, `countBackofficeAdmins`), `findClientsPage`, `findClientIdsWithCalls`, `findLastCallDatesByClientIds`, `findPhonesByClientIds`; `deleteClientDataAcrossDomains` returns `auditLogged` / `auditError`; an "Unknown" session bucket (`UNKNOWN_SESSION_ID`). **Breaking vs 1.3.66:** `aggregateClientCallStats` returns `{ rows, total }` and takes `clientIds` / `skip` / `limit`; `getAllClientsLastCallDate`, `getAllClientCredits` and `getAllClientPhoneMappings` are removed (only ever published from this branch, never on `main`). |
|
|
192
194
|
|
|
193
195
|
### 1.1.9
|
|
194
196
|
|
|
@@ -777,6 +779,24 @@ No — purely additive.
|
|
|
777
779
|
|
|
778
780
|
No API changes. **WTIS** (the only current consumer of `getScanPages`) should upgrade to 1.3.48 or later to pick up the fix.
|
|
779
781
|
|
|
782
|
+
### 1.3.52 — Client-lifecycle support for the TalkPilot Dashboard (PE-869)
|
|
783
|
+
|
|
784
|
+
Adds centralized client deletion across the talkpilot/municipal domains, with an audit trail, plus supporting getters for the client-management dashboard.
|
|
785
|
+
|
|
786
|
+
**Added**
|
|
787
|
+
|
|
788
|
+
| Area | What |
|
|
789
|
+
| --- | --- |
|
|
790
|
+
| `clients` | `clientExistsInAnyCollection` / `deleteClientDataAcrossDomains` — dynamically scan and delete a client's data across every talkpilot/municipal collection (no hardcoded collection list). `auditLogs` is always excluded, so a client's own deletion record survives its own deletion. `getAllClientCredits`. |
|
|
791
|
+
| `auditLogs` (new collection) | `insertAuditLog`, `findAuditLogs` (paginated), `countAuditLogs`. |
|
|
792
|
+
| `results` | `aggregateActiveCallsSummary`, `aggregateTodayCallsSummary`, `aggregateClientCallStats`, `aggregateSessionCallStats` — call-volume/status dashboards. `findClientIdsByCallSids` — resolves clientId per callSid, added for backOffice-server. |
|
|
793
|
+
| `phone_numbers` | Fixed `getClientPhones`/`getAllClientPhoneMappings` to deduplicate append-only history records. Added `getDistinctClientPhonePairs`, `findClientsByPhoneNumbers`, `deletePhoneNumberRecords` (clears every record for a number once released back to Twilio). |
|
|
794
|
+
|
|
795
|
+
⚠️ website-talk (`scans`/`websiteUrls`) is intentionally **not** touched by `deleteClientDataAcrossDomains` — see the Websitalk domain note above. Callers must call the external website-talk deletion API separately for a full client delete.
|
|
796
|
+
|
|
797
|
+
**Do I need to update my app?**
|
|
798
|
+
|
|
799
|
+
No — purely additive, no breaking changes. Consumers opt in by importing the new getters above.
|
|
780
800
|
### 1.3.57 — Phone-number bind getters
|
|
781
801
|
|
|
782
802
|
TalkPilot can bind any owned number to a flow without querying Mongo in the service layer. `flow_id` is matched as ObjectId or as a leftover string.
|
|
@@ -811,6 +831,32 @@ Optional `activeFlowId` on `WebsiteTalkProduct`. WTIS reads this field when it u
|
|
|
811
831
|
|
|
812
832
|
No — the field is optional. **WTIS** should upgrade to **1.3.61** so the call-script update can use it.
|
|
813
833
|
|
|
834
|
+
### 1.3.66 — Calls-based stats aggregations
|
|
835
|
+
|
|
836
|
+
Adds stats getters that read the flat `calls` collection directly instead of unwinding `results.calls[]`, with a full per-status breakdown instead of a fixed 5-status set.
|
|
837
|
+
|
|
838
|
+
**Added**
|
|
839
|
+
|
|
840
|
+
| Export | Purpose |
|
|
841
|
+
| --- | --- |
|
|
842
|
+
| `aggregateActiveCallsSummary` | Active-calls-right-now summary, from `calls`. |
|
|
843
|
+
| `aggregateTodayCallsSummary` | Status/type/minutes summary for a date range (or all-time with no range given), from `calls`. |
|
|
844
|
+
| `aggregateClientCallStats` | Per-client breakdown with a full per-status `statusCounts` map and `lastCallDate`, from `calls`. |
|
|
845
|
+
| `aggregateSessionCallStats` | Per-session breakdown with a full per-status `statusCounts` map, from `calls`. |
|
|
846
|
+
| `getAllClientsLastCallDate` | Last call date per client, from `calls`. |
|
|
847
|
+
| `findClientIdsByCallSids` | Resolves clientId per callSid, from `calls`. |
|
|
848
|
+
|
|
849
|
+
The session breakdown groups by `sessionId` (not `sessionName`) so a renamed session doesn't split its stats across two rows; the name shown is a live lookup against the current `sessions` collection.
|
|
850
|
+
|
|
851
|
+
⚠️ **Note:** the `calls` collection only reliably covers call volume from around late October 2025 onward (full per-status accuracy from mid-2026) — older history lives only in `results`. An "all time" aggregation on these getters will undercount clients whose history predates that cutover.
|
|
852
|
+
|
|
853
|
+
**Removed**
|
|
854
|
+
|
|
855
|
+
The `results`-based getters added in 1.3.52 (`aggregateActiveCallsSummary`, `aggregateTodayCallsSummary`, `aggregateClientCallStats`, `aggregateSessionCallStats`, `findClientIdsByCallSids`) are replaced by the `calls`-based getters above, under the same names. 1.3.52 was never released, so no consumer depended on them.
|
|
856
|
+
|
|
857
|
+
**Do I need to update my app?**
|
|
858
|
+
|
|
859
|
+
No — these are new getters, and the 1.3.52 ones they replace were never released, so nothing existing depends on either.
|
|
814
860
|
### 1.3.71 — Who ended the call (CP-1873)
|
|
815
861
|
|
|
816
862
|
Records who ended a connected call. Unset for calls that never connected (busy, no-answer) and for calls moved to a conference.
|
|
@@ -837,22 +883,29 @@ New `CallEndedByValues.Human` (`"human"`): a human agent ended the call, either
|
|
|
837
883
|
|
|
838
884
|
No — purely additive. **TalkPilot Server** should upgrade to **1.3.74** to write it.
|
|
839
885
|
|
|
840
|
-
### 1.3.
|
|
841
|
-
|
|
842
|
-
Append, update, or remove one note on a client/product document without replacing the rest of the `notes` array. `appendContextNoteEntry` creates the document when none exists yet. `setContextNoteEntries` still replaces the whole list.
|
|
886
|
+
### 1.3.89 — Backoffice support and the "Unknown" session bucket
|
|
843
887
|
|
|
844
888
|
**Added**
|
|
845
889
|
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
890
|
+
- Backoffice roles: `BACKOFFICE_ROLES`, `BackofficeRole`, `getBackofficeRole`, `setBackofficeRole`, `countBackofficeAdmins` (stored in `roles.backoffice`).
|
|
891
|
+
- `findClientsPage`: a page of clients with `total`, filtered by `clientId`, `clientIds`, `excludeClientIds` or a `search` on the clientId or phone numbers.
|
|
892
|
+
- Per-client lookups: `findClientIdsWithCalls`, `findLastCallDatesByClientIds`, `findPhonesByClientIds`.
|
|
893
|
+
- `deleteClientDataAcrossDomains` returns `auditLogged`, and `auditError` when it is `false`.
|
|
894
|
+
- `UNKNOWN_SESSION_ID`.
|
|
895
|
+
|
|
896
|
+
**Changed**
|
|
897
|
+
|
|
898
|
+
- `aggregateClientCallStats` accepts `clientIds`, `skip` and `limit` and returns `{ rows, total }` instead of an array. **Breaking vs 1.3.66.**
|
|
899
|
+
- `aggregateSessionCallStats`: only `isIncomingCall: true` calls are "Incoming Call"; non-incoming calls with no session get their own "Unknown" row.
|
|
900
|
+
- `getDistinctClientPhonePairs` takes an optional `clientIds`.
|
|
901
|
+
|
|
902
|
+
**Removed** (only ever in 1.3.66, never on `main`)
|
|
903
|
+
|
|
904
|
+
- `getAllClientsLastCallDate` (use `findLastCallDatesByClientIds`), `getAllClientCredits` (use `findClientsPage`, which returns `credits`) and `getAllClientPhoneMappings` (use `getDistinctClientPhonePairs` or `findPhonesByClientIds`).
|
|
852
905
|
|
|
853
906
|
**Do I need to update my app?**
|
|
854
907
|
|
|
855
|
-
|
|
908
|
+
Only if you use 1.3.66 (the backoffice): adapt to the changes above. **Backoffice** should upgrade to **1.3.89**.
|
|
856
909
|
|
|
857
910
|
## 🛠 CI/CD & deployment
|
|
858
911
|
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Collection } from "mongodb";
|
|
2
|
+
import { QueryOptions } from "../utils/query.utils";
|
|
3
|
+
import { AuditLogDoc, AuditLogEntry } from "./auditLogs.types";
|
|
4
|
+
export declare const getAuditLogsCollection: () => Collection<AuditLogEntry>;
|
|
5
|
+
export declare const insertAuditLog: (entry: AuditLogEntry) => Promise<void>;
|
|
6
|
+
export declare const findAuditLogs: (options: Pick<QueryOptions, "skip"> & Required<Pick<QueryOptions, "limit">>) => Promise<AuditLogDoc[]>;
|
|
7
|
+
export declare const countAuditLogs: () => Promise<number>;
|
|
8
|
+
//# sourceMappingURL=auditLogs.getters.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auditLogs.getters.d.ts","sourceRoot":"","sources":["../../../src/talkpilot/auditLogs/auditLogs.getters.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAErC,OAAO,EAAqB,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACvE,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAE/D,eAAO,MAAM,sBAAsB,QAAO,UAAU,CAAC,aAAa,CAEjE,CAAC;AAEF,eAAO,MAAM,cAAc,GAAU,OAAO,aAAa,KAAG,OAAO,CAAC,IAAI,CAEvE,CAAC;AAEF,eAAO,MAAM,aAAa,GACxB,SAAS,IAAI,CAAC,YAAY,EAAE,MAAM,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC,KAC1E,OAAO,CAAC,WAAW,EAAE,CAMvB,CAAC;AAEF,eAAO,MAAM,cAAc,QAAa,OAAO,CAAC,MAAM,CAErD,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.countAuditLogs = exports.findAuditLogs = exports.insertAuditLog = exports.getAuditLogsCollection = void 0;
|
|
4
|
+
const index_1 = require("../index");
|
|
5
|
+
const query_utils_1 = require("../utils/query.utils");
|
|
6
|
+
const getAuditLogsCollection = () => {
|
|
7
|
+
return (0, index_1.getDb)().collection("auditLogs");
|
|
8
|
+
};
|
|
9
|
+
exports.getAuditLogsCollection = getAuditLogsCollection;
|
|
10
|
+
const insertAuditLog = async (entry) => {
|
|
11
|
+
await (0, exports.getAuditLogsCollection)().insertOne(entry);
|
|
12
|
+
};
|
|
13
|
+
exports.insertAuditLog = insertAuditLog;
|
|
14
|
+
const findAuditLogs = async (options) => {
|
|
15
|
+
const cursor = (0, exports.getAuditLogsCollection)().find({});
|
|
16
|
+
return (0, query_utils_1.applyQueryOptions)(cursor, {
|
|
17
|
+
sort: { timestamp: -1 },
|
|
18
|
+
...options,
|
|
19
|
+
}).toArray();
|
|
20
|
+
};
|
|
21
|
+
exports.findAuditLogs = findAuditLogs;
|
|
22
|
+
const countAuditLogs = async () => {
|
|
23
|
+
return (0, exports.getAuditLogsCollection)().countDocuments({});
|
|
24
|
+
};
|
|
25
|
+
exports.countAuditLogs = countAuditLogs;
|
|
26
|
+
//# sourceMappingURL=auditLogs.getters.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auditLogs.getters.js","sourceRoot":"","sources":["../../../src/talkpilot/auditLogs/auditLogs.getters.ts"],"names":[],"mappings":";;;AACA,oCAAiC;AACjC,sDAAuE;AAGhE,MAAM,sBAAsB,GAAG,GAA8B,EAAE;IACpE,OAAO,IAAA,aAAK,GAAE,CAAC,UAAU,CAAgB,WAAW,CAAC,CAAC;AACxD,CAAC,CAAC;AAFW,QAAA,sBAAsB,0BAEjC;AAEK,MAAM,cAAc,GAAG,KAAK,EAAE,KAAoB,EAAiB,EAAE;IAC1E,MAAM,IAAA,8BAAsB,GAAE,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;AAClD,CAAC,CAAC;AAFW,QAAA,cAAc,kBAEzB;AAEK,MAAM,aAAa,GAAG,KAAK,EAChC,OAA2E,EACnD,EAAE;IAC1B,MAAM,MAAM,GAAG,IAAA,8BAAsB,GAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACjD,OAAO,IAAA,+BAAiB,EAAC,MAAM,EAAE;QAC/B,IAAI,EAAE,EAAE,SAAS,EAAE,CAAC,CAAC,EAAE;QACvB,GAAG,OAAO;KACX,CAAC,CAAC,OAAO,EAAE,CAAC;AACf,CAAC,CAAC;AARW,QAAA,aAAa,iBAQxB;AAEK,MAAM,cAAc,GAAG,KAAK,IAAqB,EAAE;IACxD,OAAO,IAAA,8BAAsB,GAAE,CAAC,cAAc,CAAC,EAAE,CAAC,CAAC;AACrD,CAAC,CAAC;AAFW,QAAA,cAAc,kBAEzB"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { WithId } from "mongodb";
|
|
2
|
+
export type AuditLogEntry = {
|
|
3
|
+
clientId: string;
|
|
4
|
+
adminEmail: string;
|
|
5
|
+
action: "delete";
|
|
6
|
+
timestamp: Date;
|
|
7
|
+
allSucceeded?: boolean;
|
|
8
|
+
failedCollections?: string[];
|
|
9
|
+
};
|
|
10
|
+
export type AuditLogDoc = WithId<AuditLogEntry>;
|
|
11
|
+
//# sourceMappingURL=auditLogs.types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auditLogs.types.d.ts","sourceRoot":"","sources":["../../../src/talkpilot/auditLogs/auditLogs.types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAEjC,MAAM,MAAM,aAAa,GAAG;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,QAAQ,CAAC;IACjB,SAAS,EAAE,IAAI,CAAC;IAChB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC9B,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,aAAa,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auditLogs.types.js","sourceRoot":"","sources":["../../../src/talkpilot/auditLogs/auditLogs.types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/talkpilot/auditLogs/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,cAAc,qBAAqB,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./auditLogs.types"), exports);
|
|
18
|
+
__exportStar(require("./auditLogs.getters"), exports);
|
|
19
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/talkpilot/auditLogs/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,oDAAkC;AAClC,sDAAoC"}
|
|
@@ -6,6 +6,14 @@ export declare const CONFERENCE_ROLE_SUPERVISOR: "supervisor";
|
|
|
6
6
|
export declare const IN_CALL_STATUSES: readonly ["in-progress", "answered"];
|
|
7
7
|
/** Terminal Twilio call statuses — no further status callbacks expected. */
|
|
8
8
|
export declare const FINAL_STATUSES: readonly ["no-answer", "completed", "busy", "failed", "canceled"];
|
|
9
|
+
/** sessionId used for calls that arrived without an existing session (incoming calls). */
|
|
10
|
+
export declare const INCOMING_SESSION_ID = "incomingCall";
|
|
11
|
+
/** sessionId used in the per-session breakdown for calls with no session that are not incoming. */
|
|
12
|
+
export declare const UNKNOWN_SESSION_ID = "unknownSession";
|
|
13
|
+
/** Twilio call statuses considered "currently active" for the live calls summary. */
|
|
14
|
+
export declare const ACTIVE_STATUSES: string[];
|
|
15
|
+
/** Lookback window for the "active calls right now" summary. */
|
|
16
|
+
export declare const ACTIVE_CALLS_WINDOW_MS: number;
|
|
9
17
|
/** Hours in a single day. */
|
|
10
18
|
export declare const HOURS_PER_DAY = 24;
|
|
11
19
|
/** Max execution time (ms) for calls statistics aggregations. */
|
|
@@ -31,7 +39,9 @@ export declare const CallStatusValues: {
|
|
|
31
39
|
readonly Failed: "failed";
|
|
32
40
|
readonly Canceled: "canceled";
|
|
33
41
|
};
|
|
34
|
-
export declare const ALL_CALL_STATUSES: ("
|
|
42
|
+
export declare const ALL_CALL_STATUSES: ("failed" | "answered" | "in-progress" | "no-answer" | "completed" | "busy" | "canceled" | "initiated" | "ringing")[];
|
|
43
|
+
/** All statuses shown in the breakdown tables — kept separate so CallStatusValues stays untouched. */
|
|
44
|
+
export declare const ALL_STATUSES_FOR_BREAKDOWN: readonly [...("failed" | "answered" | "in-progress" | "no-answer" | "completed" | "busy" | "canceled" | "initiated" | "ringing")[], "queued"];
|
|
35
45
|
/** Who ended a connected call: our platform, the AI agent (end flow), the caller, or a human agent. */
|
|
36
46
|
export declare const CallEndedByValues: {
|
|
37
47
|
readonly System: "system";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"calls.constants.d.ts","sourceRoot":"","sources":["../../../src/talkpilot/calls/calls.constants.ts"],"names":[],"mappings":"AAAA,8CAA8C;AAC9C,eAAO,MAAM,wBAAwB,EAAG,UAAmB,CAAC;AAE5D,gDAAgD;AAChD,eAAO,MAAM,0BAA0B,EAAG,YAAqB,CAAC;AAEhE,wEAAwE;AACxE,eAAO,MAAM,gBAAgB,sCAAuC,CAAC;AAErE,4EAA4E;AAC5E,eAAO,MAAM,cAAc,mEAMjB,CAAC;AAEX,6BAA6B;AAC7B,eAAO,MAAM,aAAa,KAAK,CAAC;AAEhC,iEAAiE;AACjE,eAAO,MAAM,iBAAiB,QAAS,CAAC;AAExC;;;;;GAKG;AACH,eAAO,MAAM,6BAA6B,IAAI,CAAC;AAC/C,eAAO,MAAM,4BAA4B,IAAI,CAAC;AAE9C,eAAO,MAAM,4BAA4B,IAAI,CAAC;AAC9C,eAAO,MAAM,6BAA6B,KAAK,CAAC;AAEhD,eAAO,MAAM,gBAAgB;;;;;;;;;;CAUnB,CAAC;AAEX,eAAO,MAAM,iBAAiB,uHAAkC,CAAC;AAEjE,uGAAuG;AACvG,eAAO,MAAM,iBAAiB;;;;;CAKpB,CAAC"}
|
|
1
|
+
{"version":3,"file":"calls.constants.d.ts","sourceRoot":"","sources":["../../../src/talkpilot/calls/calls.constants.ts"],"names":[],"mappings":"AAAA,8CAA8C;AAC9C,eAAO,MAAM,wBAAwB,EAAG,UAAmB,CAAC;AAE5D,gDAAgD;AAChD,eAAO,MAAM,0BAA0B,EAAG,YAAqB,CAAC;AAEhE,wEAAwE;AACxE,eAAO,MAAM,gBAAgB,sCAAuC,CAAC;AAErE,4EAA4E;AAC5E,eAAO,MAAM,cAAc,mEAMjB,CAAC;AAEX,0FAA0F;AAC1F,eAAO,MAAM,mBAAmB,iBAAiB,CAAC;AAElD,mGAAmG;AACnG,eAAO,MAAM,kBAAkB,mBAAmB,CAAC;AAEnD,qFAAqF;AACrF,eAAO,MAAM,eAAe,UAAkB,CAAC;AAE/C,gEAAgE;AAChE,eAAO,MAAM,sBAAsB,QAAqB,CAAC;AAEzD,6BAA6B;AAC7B,eAAO,MAAM,aAAa,KAAK,CAAC;AAEhC,iEAAiE;AACjE,eAAO,MAAM,iBAAiB,QAAS,CAAC;AAExC;;;;;GAKG;AACH,eAAO,MAAM,6BAA6B,IAAI,CAAC;AAC/C,eAAO,MAAM,4BAA4B,IAAI,CAAC;AAE9C,eAAO,MAAM,4BAA4B,IAAI,CAAC;AAC9C,eAAO,MAAM,6BAA6B,KAAK,CAAC;AAEhD,eAAO,MAAM,gBAAgB;;;;;;;;;;CAUnB,CAAC;AAEX,eAAO,MAAM,iBAAiB,uHAAkC,CAAC;AAEjE,sGAAsG;AACtG,eAAO,MAAM,0BAA0B,+IAA4C,CAAC;AACpF,uGAAuG;AACvG,eAAO,MAAM,iBAAiB;;;;;CAKpB,CAAC"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.CallEndedByValues = exports.ALL_CALL_STATUSES = exports.CallStatusValues = exports.TOOL_EXECUTIONS_DEFAULT_LIMIT = exports.TOOL_EXECUTIONS_DEFAULT_SKIP = exports.TREND_WEEK_BUCKET_MIN_MONTHS = exports.TREND_MONTH_BUCKET_MIN_MONTHS = exports.STATS_MAX_TIME_MS = exports.HOURS_PER_DAY = exports.FINAL_STATUSES = exports.IN_CALL_STATUSES = exports.CONFERENCE_ROLE_SUPERVISOR = exports.CONFERENCE_ROLE_CUSTOMER = void 0;
|
|
3
|
+
exports.CallEndedByValues = exports.ALL_STATUSES_FOR_BREAKDOWN = exports.ALL_CALL_STATUSES = exports.CallStatusValues = exports.TOOL_EXECUTIONS_DEFAULT_LIMIT = exports.TOOL_EXECUTIONS_DEFAULT_SKIP = exports.TREND_WEEK_BUCKET_MIN_MONTHS = exports.TREND_MONTH_BUCKET_MIN_MONTHS = exports.STATS_MAX_TIME_MS = exports.HOURS_PER_DAY = exports.ACTIVE_CALLS_WINDOW_MS = exports.ACTIVE_STATUSES = exports.UNKNOWN_SESSION_ID = exports.INCOMING_SESSION_ID = exports.FINAL_STATUSES = exports.IN_CALL_STATUSES = exports.CONFERENCE_ROLE_SUPERVISOR = exports.CONFERENCE_ROLE_CUSTOMER = void 0;
|
|
4
4
|
/** Customer role within a conference call. */
|
|
5
5
|
exports.CONFERENCE_ROLE_CUSTOMER = "customer";
|
|
6
6
|
/** Supervisor role within a conference call. */
|
|
@@ -15,6 +15,14 @@ exports.FINAL_STATUSES = [
|
|
|
15
15
|
"failed",
|
|
16
16
|
"canceled",
|
|
17
17
|
];
|
|
18
|
+
/** sessionId used for calls that arrived without an existing session (incoming calls). */
|
|
19
|
+
exports.INCOMING_SESSION_ID = "incomingCall";
|
|
20
|
+
/** sessionId used in the per-session breakdown for calls with no session that are not incoming. */
|
|
21
|
+
exports.UNKNOWN_SESSION_ID = "unknownSession";
|
|
22
|
+
/** Twilio call statuses considered "currently active" for the live calls summary. */
|
|
23
|
+
exports.ACTIVE_STATUSES = ["in-progress"];
|
|
24
|
+
/** Lookback window for the "active calls right now" summary. */
|
|
25
|
+
exports.ACTIVE_CALLS_WINDOW_MS = 2 * 60 * 60 * 1000;
|
|
18
26
|
/** Hours in a single day. */
|
|
19
27
|
exports.HOURS_PER_DAY = 24;
|
|
20
28
|
/** Max execution time (ms) for calls statistics aggregations. */
|
|
@@ -41,6 +49,8 @@ exports.CallStatusValues = {
|
|
|
41
49
|
Canceled: "canceled",
|
|
42
50
|
};
|
|
43
51
|
exports.ALL_CALL_STATUSES = Object.values(exports.CallStatusValues);
|
|
52
|
+
/** All statuses shown in the breakdown tables — kept separate so CallStatusValues stays untouched. */
|
|
53
|
+
exports.ALL_STATUSES_FOR_BREAKDOWN = [...exports.ALL_CALL_STATUSES, "queued"];
|
|
44
54
|
/** Who ended a connected call: our platform, the AI agent (end flow), the caller, or a human agent. */
|
|
45
55
|
exports.CallEndedByValues = {
|
|
46
56
|
System: "system",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"calls.constants.js","sourceRoot":"","sources":["../../../src/talkpilot/calls/calls.constants.ts"],"names":[],"mappings":";;;AAAA,8CAA8C;AACjC,QAAA,wBAAwB,GAAG,UAAmB,CAAC;AAE5D,gDAAgD;AACnC,QAAA,0BAA0B,GAAG,YAAqB,CAAC;AAEhE,wEAAwE;AAC3D,QAAA,gBAAgB,GAAG,CAAC,aAAa,EAAE,UAAU,CAAU,CAAC;AAErE,4EAA4E;AAC/D,QAAA,cAAc,GAAG;IAC5B,WAAW;IACX,WAAW;IACX,MAAM;IACN,QAAQ;IACR,UAAU;CACF,CAAC;AAEX,6BAA6B;AAChB,QAAA,aAAa,GAAG,EAAE,CAAC;AAEhC,iEAAiE;AACpD,QAAA,iBAAiB,GAAG,MAAM,CAAC;AAExC;;;;;GAKG;AACU,QAAA,6BAA6B,GAAG,CAAC,CAAC;AAClC,QAAA,4BAA4B,GAAG,CAAC,CAAC;AAEjC,QAAA,4BAA4B,GAAG,CAAC,CAAC;AACjC,QAAA,6BAA6B,GAAG,EAAE,CAAC;AAEnC,QAAA,gBAAgB,GAAG;IAC9B,SAAS,EAAE,WAAW;IACtB,OAAO,EAAE,SAAS;IAClB,UAAU,EAAE,aAAa;IACzB,QAAQ,EAAE,UAAU;IACpB,SAAS,EAAE,WAAW;IACtB,IAAI,EAAE,MAAM;IACZ,QAAQ,EAAE,WAAW;IACrB,MAAM,EAAE,QAAQ;IAChB,QAAQ,EAAE,UAAU;CACZ,CAAC;AAEE,QAAA,iBAAiB,GAAG,MAAM,CAAC,MAAM,CAAC,wBAAgB,CAAC,CAAC;AAEjE,uGAAuG;AAC1F,QAAA,iBAAiB,GAAG;IAC/B,MAAM,EAAE,QAAQ;IAChB,KAAK,EAAE,OAAO;IACd,IAAI,EAAE,MAAM;IACZ,UAAU,EAAE,YAAY;CAChB,CAAC"}
|
|
1
|
+
{"version":3,"file":"calls.constants.js","sourceRoot":"","sources":["../../../src/talkpilot/calls/calls.constants.ts"],"names":[],"mappings":";;;AAAA,8CAA8C;AACjC,QAAA,wBAAwB,GAAG,UAAmB,CAAC;AAE5D,gDAAgD;AACnC,QAAA,0BAA0B,GAAG,YAAqB,CAAC;AAEhE,wEAAwE;AAC3D,QAAA,gBAAgB,GAAG,CAAC,aAAa,EAAE,UAAU,CAAU,CAAC;AAErE,4EAA4E;AAC/D,QAAA,cAAc,GAAG;IAC5B,WAAW;IACX,WAAW;IACX,MAAM;IACN,QAAQ;IACR,UAAU;CACF,CAAC;AAEX,0FAA0F;AAC7E,QAAA,mBAAmB,GAAG,cAAc,CAAC;AAElD,mGAAmG;AACtF,QAAA,kBAAkB,GAAG,gBAAgB,CAAC;AAEnD,qFAAqF;AACxE,QAAA,eAAe,GAAG,CAAC,aAAa,CAAC,CAAC;AAE/C,gEAAgE;AACnD,QAAA,sBAAsB,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAEzD,6BAA6B;AAChB,QAAA,aAAa,GAAG,EAAE,CAAC;AAEhC,iEAAiE;AACpD,QAAA,iBAAiB,GAAG,MAAM,CAAC;AAExC;;;;;GAKG;AACU,QAAA,6BAA6B,GAAG,CAAC,CAAC;AAClC,QAAA,4BAA4B,GAAG,CAAC,CAAC;AAEjC,QAAA,4BAA4B,GAAG,CAAC,CAAC;AACjC,QAAA,6BAA6B,GAAG,EAAE,CAAC;AAEnC,QAAA,gBAAgB,GAAG;IAC9B,SAAS,EAAE,WAAW;IACtB,OAAO,EAAE,SAAS;IAClB,UAAU,EAAE,aAAa;IACzB,QAAQ,EAAE,UAAU;IACpB,SAAS,EAAE,WAAW;IACtB,IAAI,EAAE,MAAM;IACZ,QAAQ,EAAE,WAAW;IACrB,MAAM,EAAE,QAAQ;IAChB,QAAQ,EAAE,UAAU;CACZ,CAAC;AAEE,QAAA,iBAAiB,GAAG,MAAM,CAAC,MAAM,CAAC,wBAAgB,CAAC,CAAC;AAEjE,sGAAsG;AACzF,QAAA,0BAA0B,GAAG,CAAC,GAAG,yBAAiB,EAAE,QAAQ,CAAU,CAAC;AACpF,uGAAuG;AAC1F,QAAA,iBAAiB,GAAG;IAC/B,MAAM,EAAE,QAAQ;IAChB,KAAK,EAAE,OAAO;IACd,IAAI,EAAE,MAAM;IACZ,UAAU,EAAE,YAAY;CAChB,CAAC"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Call, CallDoc, CallQueryOptions, CallUpdateParams } from "../index";
|
|
2
|
-
import type { CountOpts, DateRange, ToolExecution, CallStatus, CallEndedBy } from "./calls.types";
|
|
2
|
+
import type { CountOpts, DateRange, ToolExecution, CallStatus, ActiveCallsSummary, TodayCallsSummary, ClientCallStatsPage, ClientCallStatsParams, SessionCallStats, StatsDateRangeParams, CallEndedBy } from "./calls.types";
|
|
3
3
|
import { Filter, ObjectId } from "mongodb";
|
|
4
4
|
import { DashboardHeatmapMetric } from "./dashboard/calls.dashboard.types";
|
|
5
5
|
export declare const getCallsCollection: () => import("mongodb").Collection<Call>;
|
|
@@ -41,4 +41,11 @@ export declare function getCallsStatsForDateRange(clientId: string, startStr: st
|
|
|
41
41
|
* Aggregate calls by hour for a given date (createdAt converted to given timezone). Hour format "HH:mm".
|
|
42
42
|
*/
|
|
43
43
|
export declare function getCallsHourlyAggregation(clientId: string, dateStr: string, timezone: string): Promise<DashboardHeatmapMetric[]>;
|
|
44
|
+
export declare const findClientIdsWithCalls: ({ dateFrom, dateTo, timezone, }: StatsDateRangeParams) => Promise<string[]>;
|
|
45
|
+
export declare const aggregateActiveCallsSummary: (clientId?: string) => Promise<ActiveCallsSummary>;
|
|
46
|
+
export declare const aggregateTodayCallsSummary: ({ dateFrom, dateTo, clientId, timezone, }: StatsDateRangeParams) => Promise<TodayCallsSummary>;
|
|
47
|
+
export declare const aggregateClientCallStats: ({ dateFrom, dateTo, clientId, clientIds, timezone, skip, limit, }: ClientCallStatsParams) => Promise<ClientCallStatsPage>;
|
|
48
|
+
export declare const aggregateSessionCallStats: ({ dateFrom, dateTo, clientId, timezone, }: StatsDateRangeParams) => Promise<SessionCallStats[]>;
|
|
49
|
+
export declare const findLastCallDatesByClientIds: (clientIds: string[]) => Promise<Map<string, Date>>;
|
|
50
|
+
export declare const findClientIdsByCallSids: (callSids: string[]) => Promise<Map<string, string>>;
|
|
44
51
|
//# sourceMappingURL=calls.getters.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"calls.getters.d.ts","sourceRoot":"","sources":["../../../src/talkpilot/calls/calls.getters.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,IAAI,EACJ,OAAO,EACP,gBAAgB,EAChB,gBAAgB,
|
|
1
|
+
{"version":3,"file":"calls.getters.d.ts","sourceRoot":"","sources":["../../../src/talkpilot/calls/calls.getters.ts"],"names":[],"mappings":"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"}
|
|
@@ -33,7 +33,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.range = exports.countCalls = exports.findCallsByQuery = exports.getCallsByClientAndDateRange = exports.pushToolExecution = exports.setCallEndedBy = exports.updateCallStatusByCallSid = exports.getToolExecutionsByCallSid = exports.updateCallByCallSid = exports.createCallDoc = exports.getCallsByFlow = exports.getCallsByClient = exports.getCallsByPhoneNumber = exports.getCallByCallSid = exports.getCallsCollection = void 0;
|
|
36
|
+
exports.findClientIdsByCallSids = exports.findLastCallDatesByClientIds = exports.aggregateSessionCallStats = exports.aggregateClientCallStats = exports.aggregateTodayCallsSummary = exports.aggregateActiveCallsSummary = exports.findClientIdsWithCalls = exports.range = exports.countCalls = exports.findCallsByQuery = exports.getCallsByClientAndDateRange = exports.pushToolExecution = exports.setCallEndedBy = exports.updateCallStatusByCallSid = exports.getToolExecutionsByCallSid = exports.updateCallByCallSid = exports.createCallDoc = exports.getCallsByFlow = exports.getCallsByClient = exports.getCallsByPhoneNumber = exports.getCallByCallSid = exports.getCallsCollection = void 0;
|
|
37
37
|
exports.countCallsByPhoneInRange = countCallsByPhoneInRange;
|
|
38
38
|
exports.startOfDay = startOfDay;
|
|
39
39
|
exports.addDays = addDays;
|
|
@@ -43,6 +43,7 @@ const index_1 = require("../index");
|
|
|
43
43
|
const calls_constants_1 = require("./calls.constants");
|
|
44
44
|
const process = __importStar(require("node:process"));
|
|
45
45
|
const query_utils_1 = require("../utils/query.utils");
|
|
46
|
+
const date_utils_1 = require("../../utils/date.utils");
|
|
46
47
|
const getCallsCollection = () => {
|
|
47
48
|
return (0, index_1.getDb)().collection("calls");
|
|
48
49
|
};
|
|
@@ -246,4 +247,218 @@ async function getCallsHourlyAggregation(clientId, dateStr, timezone) {
|
|
|
246
247
|
calls: r.calls,
|
|
247
248
|
}));
|
|
248
249
|
}
|
|
250
|
+
// One $sum/$cond accumulator per status in ALL_STATUSES_FOR_BREAKDOWN.
|
|
251
|
+
const buildStatusCountFields = (field) => Object.fromEntries(calls_constants_1.ALL_STATUSES_FOR_BREAKDOWN.map((status) => [
|
|
252
|
+
status,
|
|
253
|
+
{ $sum: { $cond: [{ $eq: [field, status] }, 1, 0] } },
|
|
254
|
+
]));
|
|
255
|
+
const extractStatusCounts = (row) => Object.fromEntries(calls_constants_1.ALL_STATUSES_FOR_BREAKDOWN.map((status) => [status, row[status] ?? 0]));
|
|
256
|
+
const dateRangeFilter = (dateFrom, dateTo, timezone) => {
|
|
257
|
+
const today = (0, date_utils_1.formatYmdInTz)(new Date(), timezone);
|
|
258
|
+
return {
|
|
259
|
+
start: (0, date_utils_1.startOfCalendarDayInTz)(dateFrom ?? today, timezone),
|
|
260
|
+
end: (0, date_utils_1.endOfCalendarDayInTz)(dateTo ?? today, timezone),
|
|
261
|
+
};
|
|
262
|
+
};
|
|
263
|
+
const buildCallsStatsMatch = ({ dateFrom, dateTo, clientId, clientIds, timezone = "UTC", }) => {
|
|
264
|
+
const range = dateFrom || dateTo ? dateRangeFilter(dateFrom, dateTo, timezone) : null;
|
|
265
|
+
const clientConditions = [
|
|
266
|
+
...(clientId ? [{ clientId }] : []),
|
|
267
|
+
...(clientIds ? [{ clientId: { $in: clientIds } }] : []),
|
|
268
|
+
];
|
|
269
|
+
return {
|
|
270
|
+
...(range ? { createdAt: { $gte: range.start, $lte: range.end } } : {}),
|
|
271
|
+
...(clientConditions.length > 0 ? { $and: clientConditions } : {}),
|
|
272
|
+
};
|
|
273
|
+
};
|
|
274
|
+
const findClientIdsWithCalls = async ({ dateFrom, dateTo, timezone = "UTC", }) => (0, exports.getCallsCollection)().distinct("clientId", buildCallsStatsMatch({ dateFrom, dateTo, timezone }));
|
|
275
|
+
exports.findClientIdsWithCalls = findClientIdsWithCalls;
|
|
276
|
+
const aggregateActiveCallsSummary = async (clientId) => {
|
|
277
|
+
const coll = (0, exports.getCallsCollection)();
|
|
278
|
+
const twoHoursAgo = new Date(Date.now() - calls_constants_1.ACTIVE_CALLS_WINDOW_MS);
|
|
279
|
+
const match = {
|
|
280
|
+
status: { $in: calls_constants_1.ACTIVE_STATUSES },
|
|
281
|
+
updatedAt: { $gte: twoHoursAgo },
|
|
282
|
+
...(clientId ? { clientId } : {}),
|
|
283
|
+
};
|
|
284
|
+
const facetResult = await coll
|
|
285
|
+
.aggregate([
|
|
286
|
+
{ $match: match },
|
|
287
|
+
{
|
|
288
|
+
$facet: {
|
|
289
|
+
counts: [
|
|
290
|
+
{
|
|
291
|
+
$group: {
|
|
292
|
+
_id: null,
|
|
293
|
+
total: { $sum: 1 },
|
|
294
|
+
incoming: { $sum: { $cond: ["$isIncomingCall", 1, 0] } },
|
|
295
|
+
outgoing: { $sum: { $cond: ["$isOutgoingCall", 1, 0] } },
|
|
296
|
+
},
|
|
297
|
+
},
|
|
298
|
+
],
|
|
299
|
+
activeClients: [{ $group: { _id: "$clientId" } }],
|
|
300
|
+
},
|
|
301
|
+
},
|
|
302
|
+
])
|
|
303
|
+
.next();
|
|
304
|
+
const counts = facetResult?.counts?.[0];
|
|
305
|
+
return {
|
|
306
|
+
total: counts?.total ?? 0,
|
|
307
|
+
incoming: counts?.incoming ?? 0,
|
|
308
|
+
outgoing: counts?.outgoing ?? 0,
|
|
309
|
+
activeClientIds: (facetResult?.activeClients ?? []).map((d) => d._id || "Unknown"),
|
|
310
|
+
};
|
|
311
|
+
};
|
|
312
|
+
exports.aggregateActiveCallsSummary = aggregateActiveCallsSummary;
|
|
313
|
+
const aggregateTodayCallsSummary = async ({ dateFrom, dateTo, clientId, timezone = "UTC", }) => {
|
|
314
|
+
const coll = (0, exports.getCallsCollection)();
|
|
315
|
+
const match = buildCallsStatsMatch({ dateFrom, dateTo, clientId, timezone });
|
|
316
|
+
const facetResult = await coll
|
|
317
|
+
.aggregate([
|
|
318
|
+
{ $match: match },
|
|
319
|
+
{
|
|
320
|
+
$facet: {
|
|
321
|
+
byStatus: [{ $group: { _id: "$status", count: { $sum: 1 } } }],
|
|
322
|
+
byType: [
|
|
323
|
+
{
|
|
324
|
+
$group: {
|
|
325
|
+
_id: { $cond: ["$isIncomingCall", "incoming", "outgoing"] },
|
|
326
|
+
count: { $sum: 1 },
|
|
327
|
+
},
|
|
328
|
+
},
|
|
329
|
+
],
|
|
330
|
+
total: [{ $group: { _id: null, count: { $sum: 1 } } }],
|
|
331
|
+
totalSeconds: [{ $group: { _id: null, seconds: { $sum: "$callLength" } } }],
|
|
332
|
+
},
|
|
333
|
+
},
|
|
334
|
+
])
|
|
335
|
+
.next();
|
|
336
|
+
const byStatus = {};
|
|
337
|
+
(facetResult?.byStatus ?? []).forEach((s) => {
|
|
338
|
+
byStatus[s._id] = s.count;
|
|
339
|
+
});
|
|
340
|
+
const byType = {};
|
|
341
|
+
(facetResult?.byType ?? []).forEach((t) => {
|
|
342
|
+
byType[t._id] = t.count;
|
|
343
|
+
});
|
|
344
|
+
const totalSeconds = facetResult?.totalSeconds?.[0]?.seconds ?? 0;
|
|
345
|
+
return {
|
|
346
|
+
total: facetResult?.total?.[0]?.count ?? 0,
|
|
347
|
+
incoming: byType.incoming ?? 0,
|
|
348
|
+
outgoing: byType.outgoing ?? 0,
|
|
349
|
+
completed: byStatus.completed ?? 0,
|
|
350
|
+
failed: byStatus.failed ?? 0,
|
|
351
|
+
busy: byStatus.busy ?? 0,
|
|
352
|
+
noAnswer: byStatus["no-answer"] ?? 0,
|
|
353
|
+
totalMinutes: Math.ceil(totalSeconds / 60),
|
|
354
|
+
};
|
|
355
|
+
};
|
|
356
|
+
exports.aggregateTodayCallsSummary = aggregateTodayCallsSummary;
|
|
357
|
+
const aggregateClientCallStats = async ({ dateFrom, dateTo, clientId, clientIds, timezone = "UTC", skip = 0, limit, }) => {
|
|
358
|
+
const [result] = await (0, exports.getCallsCollection)()
|
|
359
|
+
.aggregate([
|
|
360
|
+
{ $match: buildCallsStatsMatch({ dateFrom, dateTo, clientId, clientIds, timezone }) },
|
|
361
|
+
{
|
|
362
|
+
$group: {
|
|
363
|
+
_id: "$clientId",
|
|
364
|
+
totalCalls: { $sum: 1 },
|
|
365
|
+
...buildStatusCountFields("$status"),
|
|
366
|
+
totalSeconds: { $sum: "$callLength" },
|
|
367
|
+
lastCallDate: { $max: "$createdAt" },
|
|
368
|
+
},
|
|
369
|
+
},
|
|
370
|
+
{
|
|
371
|
+
$lookup: {
|
|
372
|
+
from: (0, index_1.getClientsCollection)().collectionName,
|
|
373
|
+
localField: "_id",
|
|
374
|
+
foreignField: "clientId",
|
|
375
|
+
as: "client",
|
|
376
|
+
},
|
|
377
|
+
},
|
|
378
|
+
{ $match: { "client.0": { $exists: true } } },
|
|
379
|
+
{ $project: { client: 0 } },
|
|
380
|
+
{ $sort: { totalCalls: -1, _id: 1 } },
|
|
381
|
+
{
|
|
382
|
+
$facet: {
|
|
383
|
+
rows: [{ $skip: skip }, ...(limit ? [{ $limit: limit }] : [])],
|
|
384
|
+
total: [{ $count: "count" }],
|
|
385
|
+
},
|
|
386
|
+
},
|
|
387
|
+
])
|
|
388
|
+
.toArray();
|
|
389
|
+
return {
|
|
390
|
+
rows: (result?.rows ?? []).map((r) => ({
|
|
391
|
+
clientId: r._id,
|
|
392
|
+
totalCalls: r.totalCalls,
|
|
393
|
+
statusCounts: extractStatusCounts(r),
|
|
394
|
+
totalMinutes: Math.ceil((r.totalSeconds || 0) / 60),
|
|
395
|
+
lastCallDate: r.lastCallDate,
|
|
396
|
+
})),
|
|
397
|
+
total: result?.total[0]?.count ?? 0,
|
|
398
|
+
};
|
|
399
|
+
};
|
|
400
|
+
exports.aggregateClientCallStats = aggregateClientCallStats;
|
|
401
|
+
const aggregateSessionCallStats = async ({ dateFrom, dateTo, clientId, timezone = "UTC", }) => {
|
|
402
|
+
const coll = (0, exports.getCallsCollection)();
|
|
403
|
+
const match = buildCallsStatsMatch({ dateFrom, dateTo, clientId, timezone });
|
|
404
|
+
const sessionGroupKey = {
|
|
405
|
+
$cond: [
|
|
406
|
+
{ $eq: ["$isIncomingCall", true] },
|
|
407
|
+
calls_constants_1.INCOMING_SESSION_ID,
|
|
408
|
+
{ $ifNull: ["$sessionId", calls_constants_1.UNKNOWN_SESSION_ID] },
|
|
409
|
+
],
|
|
410
|
+
};
|
|
411
|
+
const rows = await coll
|
|
412
|
+
.aggregate([
|
|
413
|
+
{ $match: match },
|
|
414
|
+
{
|
|
415
|
+
$group: {
|
|
416
|
+
_id: sessionGroupKey,
|
|
417
|
+
totalCalls: { $sum: 1 },
|
|
418
|
+
...buildStatusCountFields("$status"),
|
|
419
|
+
},
|
|
420
|
+
},
|
|
421
|
+
{
|
|
422
|
+
$lookup: {
|
|
423
|
+
from: "sessions",
|
|
424
|
+
localField: "_id",
|
|
425
|
+
foreignField: "_id",
|
|
426
|
+
as: "sessionDoc",
|
|
427
|
+
},
|
|
428
|
+
},
|
|
429
|
+
{ $sort: { totalCalls: -1 } },
|
|
430
|
+
])
|
|
431
|
+
.toArray();
|
|
432
|
+
return rows.map((r) => ({
|
|
433
|
+
sessionId: r._id.toString(),
|
|
434
|
+
// No session, or a deleted/unnamed one, has no name to look up: that is "Unknown".
|
|
435
|
+
sessionName: r._id === calls_constants_1.INCOMING_SESSION_ID
|
|
436
|
+
? "Incoming Call"
|
|
437
|
+
: r.sessionDoc[0]?.session_name || "Unknown",
|
|
438
|
+
totalCalls: r.totalCalls,
|
|
439
|
+
statusCounts: extractStatusCounts(r),
|
|
440
|
+
}));
|
|
441
|
+
};
|
|
442
|
+
exports.aggregateSessionCallStats = aggregateSessionCallStats;
|
|
443
|
+
const findLastCallDatesByClientIds = async (clientIds) => {
|
|
444
|
+
if (clientIds.length === 0)
|
|
445
|
+
return new Map();
|
|
446
|
+
const rows = await (0, exports.getCallsCollection)()
|
|
447
|
+
.aggregate([
|
|
448
|
+
{ $match: { clientId: { $in: clientIds } } },
|
|
449
|
+
{ $group: { _id: "$clientId", lastCallDate: { $max: "$createdAt" } } },
|
|
450
|
+
])
|
|
451
|
+
.toArray();
|
|
452
|
+
return new Map(rows.map((r) => [r._id, r.lastCallDate]));
|
|
453
|
+
};
|
|
454
|
+
exports.findLastCallDatesByClientIds = findLastCallDatesByClientIds;
|
|
455
|
+
const findClientIdsByCallSids = async (callSids) => {
|
|
456
|
+
if (callSids.length === 0)
|
|
457
|
+
return new Map();
|
|
458
|
+
const docs = await (0, exports.getCallsCollection)()
|
|
459
|
+
.find({ callSid: { $in: callSids } }, { projection: { _id: 0, clientId: 1, callSid: 1 } })
|
|
460
|
+
.toArray();
|
|
461
|
+
return new Map(docs.map((d) => [d.callSid, d.clientId]));
|
|
462
|
+
};
|
|
463
|
+
exports.findClientIdsByCallSids = findClientIdsByCallSids;
|
|
249
464
|
//# sourceMappingURL=calls.getters.js.map
|