@twentyhq/call-recorder 1.0.1 → 1.0.2
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/manifest.json +297 -0
- package/package.json +2 -2
- package/src/front-components/calendar-event-recording.front-component.mjs +269 -0
- package/src/front-components/calendar-event-recording.front-component.mjs.map +7 -0
- package/src/logic-functions/process-recall-webhook.mjs +1744 -0
- package/src/logic-functions/process-recall-webhook.mjs.map +7 -0
- package/src/logic-functions/recall-webhook.mjs +391 -0
- package/src/logic-functions/recall-webhook.mjs.map +7 -0
- package/src/logic-functions/reconcile-call-recorder-calendar-event.mjs +1602 -0
- package/src/logic-functions/reconcile-call-recorder-calendar-event.mjs.map +7 -0
- package/src/logic-functions/reconcile-stale-bot-state.mjs +2268 -0
- package/src/logic-functions/reconcile-stale-bot-state.mjs.map +7 -0
- package/.env.example +0 -5
- package/.nvmrc +0 -1
- package/.oxlintrc.json +0 -20
- package/AGENTS.md +0 -67
- package/CLAUDE.md +0 -67
- package/README.md +0 -24
- package/SETUP.md +0 -95
- package/src/__tests__/global-setup.ts +0 -100
- package/src/__tests__/schema.integration-test.ts +0 -104
- package/src/application-config.ts +0 -96
- package/src/constants/__tests__/call-recording-field-universal-identifiers.test.ts +0 -19
- package/src/constants/app-description.ts +0 -2
- package/src/constants/app-display-name.ts +0 -1
- package/src/constants/application-universal-identifier.ts +0 -2
- package/src/constants/calendar-event-reconciliation-logic-function-universal-identifier.ts +0 -2
- package/src/constants/calendar-event-record-page-layout-universal-identifier.ts +0 -2
- package/src/constants/calendar-event-recording-front-component-universal-identifier.ts +0 -2
- package/src/constants/calendar-event-recording-page-layout-tab-universal-identifier.ts +0 -2
- package/src/constants/calendar-event-recording-page-layout-widget-universal-identifier.ts +0 -2
- package/src/constants/call-recorder-everyone-left-timeout-seconds-app-variable-universal-identifier.ts +0 -2
- package/src/constants/call-recorder-failure-reason-on-call-recording-field-universal-identifier.ts +0 -2
- package/src/constants/call-recorder-join-early-minutes-app-variable-universal-identifier.ts +0 -2
- package/src/constants/call-recorder-name-app-variable-universal-identifier.ts +0 -2
- package/src/constants/call-recorder-noone-joined-timeout-seconds-app-variable-universal-identifier.ts +0 -2
- package/src/constants/call-recorder-preference-off-option-id.ts +0 -2
- package/src/constants/call-recorder-preference-on-calendar-event-field-universal-identifier.ts +0 -2
- package/src/constants/call-recorder-preference-on-calendar-event-view-field-universal-identifier.ts +0 -2
- package/src/constants/call-recorder-preference-on-option-id.ts +0 -2
- package/src/constants/call-recorder-preference.ts +0 -4
- package/src/constants/call-recorder-waiting-room-timeout-seconds-app-variable-universal-identifier.ts +0 -2
- package/src/constants/call-recording-audio-field-universal-identifier.ts +0 -2
- package/src/constants/call-recording-video-field-universal-identifier.ts +0 -2
- package/src/constants/default-role-universal-identifier.ts +0 -2
- package/src/constants/process-recall-webhook-logic-function-universal-identifier.ts +0 -2
- package/src/constants/recall-webhook-logic-function-universal-identifier.ts +0 -2
- package/src/constants/stale-bot-state-logic-function-universal-identifier.ts +0 -2
- package/src/default-role.ts +0 -69
- package/src/fields/call-recorder-failure-reason-on-call-recording.field.ts +0 -22
- package/src/fields/call-recorder-preference-on-calendar-event.field.ts +0 -41
- package/src/front-components/calendar-event-recording.front-component.tsx +0 -13
- package/src/front-components/components/CalendarEventRecording.tsx +0 -39
- package/src/front-components/components/CalendarEventRecordingBody.tsx +0 -96
- package/src/front-components/components/CalendarEventRecordingContent.tsx +0 -111
- package/src/front-components/components/RecordingTranscript.tsx +0 -92
- package/src/front-components/components/RecordingVideoPlayer.tsx +0 -52
- package/src/front-components/components/TranscriptEntryList.tsx +0 -61
- package/src/front-components/components/TranscriptEntryListItem.tsx +0 -115
- package/src/front-components/components/TranscriptErrorBox.tsx +0 -48
- package/src/front-components/components/TranscriptSpeakerAvatar.tsx +0 -141
- package/src/front-components/components/TranscriptSpeakerChip.tsx +0 -51
- package/src/front-components/constants/recording-theme-css-variables.ts +0 -40
- package/src/front-components/hooks/use-calendar-event-participants.ts +0 -172
- package/src/front-components/hooks/use-calendar-event-recording.ts +0 -155
- package/src/front-components/types/calendar-event-participant-by-speaker-name.type.ts +0 -6
- package/src/front-components/types/calendar-event-recording-participant.type.ts +0 -7
- package/src/front-components/types/transcript-entry.type.ts +0 -13
- package/src/front-components/utils/__tests__/find-active-transcript-entry-index.test.ts +0 -66
- package/src/front-components/utils/__tests__/format-transcript-timestamp.test.ts +0 -29
- package/src/front-components/utils/__tests__/get-speaker-name-match-keys.test.ts +0 -22
- package/src/front-components/utils/__tests__/parse-transcript-entries.test.ts +0 -162
- package/src/front-components/utils/build-calendar-event-participant-by-speaker-name.util.ts +0 -45
- package/src/front-components/utils/find-active-transcript-entry-index.util.ts +0 -77
- package/src/front-components/utils/format-transcript-timestamp.util.ts +0 -16
- package/src/front-components/utils/get-absolute-avatar-url.util.ts +0 -48
- package/src/front-components/utils/get-calendar-event-participant-for-speaker-name.util.ts +0 -24
- package/src/front-components/utils/get-speaker-name-match-keys.util.ts +0 -64
- package/src/front-components/utils/get-video-file-extension.util.ts +0 -23
- package/src/front-components/utils/parse-transcript-entries.util.ts +0 -85
- package/src/logic-functions/__tests__/process-recall-webhook.test.ts +0 -62
- package/src/logic-functions/__tests__/recall-webhook.test.ts +0 -180
- package/src/logic-functions/constants/call-recorder-everyone-left-timeout-seconds-env-var-name.ts +0 -2
- package/src/logic-functions/constants/call-recorder-everyone-left-timeout-seconds.ts +0 -1
- package/src/logic-functions/constants/call-recorder-join-early-minutes-env-var-name.ts +0 -2
- package/src/logic-functions/constants/call-recorder-name-env-var-name.ts +0 -1
- package/src/logic-functions/constants/call-recorder-noone-joined-timeout-seconds-env-var-name.ts +0 -2
- package/src/logic-functions/constants/call-recorder-noone-joined-timeout-seconds.ts +0 -1
- package/src/logic-functions/constants/call-recorder-recording-retention-hours-env-var-name.ts +0 -2
- package/src/logic-functions/constants/call-recorder-waiting-room-timeout-seconds-env-var-name.ts +0 -2
- package/src/logic-functions/constants/call-recorder-waiting-room-timeout-seconds.ts +0 -1
- package/src/logic-functions/constants/call-recording-micro-credits-per-hour.ts +0 -1
- package/src/logic-functions/constants/call-recording-request-status.ts +0 -5
- package/src/logic-functions/constants/call-recording-status.ts +0 -9
- package/src/logic-functions/constants/default-call-recorder-join-early-minutes.ts +0 -1
- package/src/logic-functions/constants/default-call-recorder-name.ts +0 -1
- package/src/logic-functions/constants/default-call-recorder-recording-retention-hours.ts +0 -2
- package/src/logic-functions/constants/default-recall-region.ts +0 -1
- package/src/logic-functions/constants/milliseconds-per-minute.ts +0 -1
- package/src/logic-functions/constants/non-terminal-call-recording-statuses.ts +0 -8
- package/src/logic-functions/constants/recall-api-key-env-var-name.ts +0 -1
- package/src/logic-functions/constants/recall-api-max-attempts.ts +0 -1
- package/src/logic-functions/constants/recall-api-retry-delay-ms.ts +0 -1
- package/src/logic-functions/constants/recall-bot-automatic-leave.ts +0 -74
- package/src/logic-functions/constants/recall-bot-everyone-left-min-activate-after-seconds.ts +0 -1
- package/src/logic-functions/constants/recall-bot-recording-config.ts +0 -34
- package/src/logic-functions/constants/recall-region-env-var-name.ts +0 -1
- package/src/logic-functions/constants/recall-webhook-secret-env-var-name.ts +0 -1
- package/src/logic-functions/constants/restricted-field-placeholder.ts +0 -3
- package/src/logic-functions/constants/stale-bot-state-cron-pattern.ts +0 -1
- package/src/logic-functions/constants/twenty-page-size.ts +0 -1
- package/src/logic-functions/data/__tests__/complete-call-recording-ingestion.test.ts +0 -55
- package/src/logic-functions/data/__tests__/fetch-all-nodes.test.ts +0 -43
- package/src/logic-functions/data/__tests__/get-current-workspace-id.test.ts +0 -38
- package/src/logic-functions/data/__tests__/strip-restricted-field-value.test.ts +0 -22
- package/src/logic-functions/data/complete-call-recording-ingestion.util.ts +0 -24
- package/src/logic-functions/data/create-call-recording.util.ts +0 -41
- package/src/logic-functions/data/fetch-all-nodes.util.ts +0 -44
- package/src/logic-functions/data/fetch-calendar-events-by-filter.util.ts +0 -80
- package/src/logic-functions/data/fetch-calendar-events-by-ids.util.ts +0 -20
- package/src/logic-functions/data/fetch-calendar-events-by-starts-at-values.util.ts +0 -19
- package/src/logic-functions/data/find-call-recordings-by-calendar-event-ids.util.ts +0 -17
- package/src/logic-functions/data/find-call-recordings-by-filter.util.ts +0 -102
- package/src/logic-functions/data/find-call-recordings-by-ids.util.ts +0 -17
- package/src/logic-functions/data/find-open-scheduled-call-recordings.util.ts +0 -14
- package/src/logic-functions/data/get-current-workspace-id.util.ts +0 -36
- package/src/logic-functions/data/strip-restricted-field-value.util.ts +0 -6
- package/src/logic-functions/data/update-call-recording.util.ts +0 -24
- package/src/logic-functions/domain/__tests__/build-call-recorder-policy-result.test.ts +0 -47
- package/src/logic-functions/domain/__tests__/compute-call-recording-charge.test.ts +0 -71
- package/src/logic-functions/domain/__tests__/compute-call-recording-id-for-meeting.test.ts +0 -37
- package/src/logic-functions/domain/__tests__/compute-real-meeting-key.test.ts +0 -88
- package/src/logic-functions/domain/__tests__/is-call-recording-ingestion-complete.test.ts +0 -59
- package/src/logic-functions/domain/__tests__/is-call-recording-status-downgrade.test.ts +0 -37
- package/src/logic-functions/domain/__tests__/resolve-call-recorder-policy-result.test.ts +0 -120
- package/src/logic-functions/domain/__tests__/should-complete-call-recording-ingestion.test.ts +0 -102
- package/src/logic-functions/domain/aggregate-call-recorder-policy-results-by-meeting.util.ts +0 -42
- package/src/logic-functions/domain/build-call-recorder-policy-result.util.ts +0 -53
- package/src/logic-functions/domain/build-failed-transcript-marker.util.ts +0 -13
- package/src/logic-functions/domain/build-pending-transcript-marker.util.ts +0 -13
- package/src/logic-functions/domain/build-recall-routing-metadata.util.ts +0 -12
- package/src/logic-functions/domain/build-transcript-failure-reason.util.ts +0 -7
- package/src/logic-functions/domain/compute-call-recording-charge.util.ts +0 -41
- package/src/logic-functions/domain/compute-call-recording-id-for-meeting.util.ts +0 -16
- package/src/logic-functions/domain/compute-real-meeting-key.util.ts +0 -48
- package/src/logic-functions/domain/compute-recall-bot-join-at.util.ts +0 -34
- package/src/logic-functions/domain/is-call-recording-ingestion-complete.util.ts +0 -19
- package/src/logic-functions/domain/is-call-recording-status-downgrade.util.ts +0 -37
- package/src/logic-functions/domain/is-recall-recording-done-signal.util.ts +0 -13
- package/src/logic-functions/domain/map-recall-status-code-to-call-recording-status.util.ts +0 -26
- package/src/logic-functions/domain/parse-transcript-marker.util.ts +0 -29
- package/src/logic-functions/domain/resolve-call-recorder-policy-result.util.ts +0 -72
- package/src/logic-functions/domain/should-complete-call-recording-ingestion.util.ts +0 -32
- package/src/logic-functions/flows/__tests__/charge-completed-call-recording.test.ts +0 -45
- package/src/logic-functions/flows/__tests__/complete-and-charge-call-recording.test.ts +0 -61
- package/src/logic-functions/flows/__tests__/converge-diverged-call-recordings.test.ts +0 -727
- package/src/logic-functions/flows/__tests__/download-transcript.test.ts +0 -74
- package/src/logic-functions/flows/__tests__/handle-recall-webhook.test.ts +0 -1301
- package/src/logic-functions/flows/__tests__/heal-call-recordings-missing-bot.test.ts +0 -225
- package/src/logic-functions/flows/__tests__/ingest-call-recording-media.test.ts +0 -153
- package/src/logic-functions/flows/__tests__/reap-orphaned-call-recorders.test.ts +0 -425
- package/src/logic-functions/flows/__tests__/reconcile-call-recorder.test.ts +0 -1007
- package/src/logic-functions/flows/cancel-call-recording-request.util.ts +0 -46
- package/src/logic-functions/flows/charge-completed-call-recording.util.ts +0 -31
- package/src/logic-functions/flows/complete-and-charge-call-recording.util.ts +0 -29
- package/src/logic-functions/flows/converge-diverged-call-recordings-result.type.ts +0 -8
- package/src/logic-functions/flows/converge-diverged-call-recordings.util.ts +0 -447
- package/src/logic-functions/flows/download-transcript.util.ts +0 -67
- package/src/logic-functions/flows/ensure-call-recorder.util.ts +0 -73
- package/src/logic-functions/flows/handle-recall-webhook.util.ts +0 -672
- package/src/logic-functions/flows/heal-call-recordings-missing-bot.util.ts +0 -82
- package/src/logic-functions/flows/ingest-call-recording-media.util.ts +0 -128
- package/src/logic-functions/flows/persist-call-recording-progress.util.ts +0 -58
- package/src/logic-functions/flows/reap-orphaned-call-recorders.util.ts +0 -183
- package/src/logic-functions/flows/reconcile-call-recorder.util.ts +0 -495
- package/src/logic-functions/flows/reconcile-call-recording-transcript-artifact-result.type.ts +0 -11
- package/src/logic-functions/flows/reconcile-call-recording-transcript-artifact.util.ts +0 -182
- package/src/logic-functions/flows/reschedule-call-recording-bot.util.ts +0 -69
- package/src/logic-functions/process-recall-webhook.ts +0 -23
- package/src/logic-functions/recall-api/__tests__/extract-recall-bot-convergence.test.ts +0 -153
- package/src/logic-functions/recall-api/__tests__/extract-recall-media-urls.test.ts +0 -67
- package/src/logic-functions/recall-api/__tests__/recall-bot-api.test.ts +0 -744
- package/src/logic-functions/recall-api/__tests__/verify-recall-webhook-signature.test.ts +0 -122
- package/src/logic-functions/recall-api/cancel-recall-bot.util.ts +0 -28
- package/src/logic-functions/recall-api/create-async-recall-transcript.util.ts +0 -47
- package/src/logic-functions/recall-api/eject-recall-bot.util.ts +0 -28
- package/src/logic-functions/recall-api/extract-recall-bot-convergence.util.ts +0 -149
- package/src/logic-functions/recall-api/extract-recall-bot-id.util.ts +0 -10
- package/src/logic-functions/recall-api/extract-recall-media-urls.util.ts +0 -30
- package/src/logic-functions/recall-api/extract-twenty-workspace-id-from-recall-webhook.util.ts +0 -8
- package/src/logic-functions/recall-api/get-recall-api-config.util.ts +0 -59
- package/src/logic-functions/recall-api/get-recall-bot.util.ts +0 -42
- package/src/logic-functions/recall-api/get-recall-recording.util.ts +0 -31
- package/src/logic-functions/recall-api/get-recall-webhook-bot-metadata.util.ts +0 -18
- package/src/logic-functions/recall-api/list-recall-transcripts.util.ts +0 -141
- package/src/logic-functions/recall-api/list-scheduled-recall-bots.util.ts +0 -106
- package/src/logic-functions/recall-api/normalize-recall-timestamp.util.ts +0 -14
- package/src/logic-functions/recall-api/parse-recall-webhook-event.util.ts +0 -88
- package/src/logic-functions/recall-api/recall-bot-api-request.util.ts +0 -165
- package/src/logic-functions/recall-api/recall-transcript-summary.type.ts +0 -5
- package/src/logic-functions/recall-api/reschedule-recall-bot.util.ts +0 -56
- package/src/logic-functions/recall-api/retrieve-recall-transcript.util.ts +0 -71
- package/src/logic-functions/recall-api/schedule-recall-bot.util.ts +0 -68
- package/src/logic-functions/recall-api/verify-recall-webhook-signature.util.ts +0 -109
- package/src/logic-functions/recall-webhook.ts +0 -90
- package/src/logic-functions/reconcile-call-recorder-calendar-event.ts +0 -178
- package/src/logic-functions/reconcile-stale-bot-state.ts +0 -106
- package/src/logic-functions/types/calendar-event-record.type.ts +0 -5
- package/src/logic-functions/types/call-recorder-policy-calendar-event-input.type.ts +0 -10
- package/src/logic-functions/types/call-recorder-policy-input.type.ts +0 -9
- package/src/logic-functions/types/call-recorder-policy-not-required-reason.type.ts +0 -5
- package/src/logic-functions/types/call-recorder-policy-required-reason.type.ts +0 -1
- package/src/logic-functions/types/call-recorder-policy-result-for-calendar-event.type.ts +0 -9
- package/src/logic-functions/types/call-recorder-policy-result-for-meeting.type.ts +0 -6
- package/src/logic-functions/types/call-recorder-policy-result.type.ts +0 -12
- package/src/logic-functions/types/call-recorder-reconciliation-result.type.ts +0 -16
- package/src/logic-functions/types/call-recording-media-file.type.ts +0 -1
- package/src/logic-functions/types/call-recording-record.type.ts +0 -15
- package/src/logic-functions/types/call-recording-update-fields.type.ts +0 -20
- package/src/logic-functions/types/files-field-value.type.ts +0 -1
- package/src/logic-functions/types/meeting-recording.type.ts +0 -7
- package/src/logic-functions/types/recall-bot-operation-result.type.ts +0 -19
- package/src/logic-functions/types/recall-routing-metadata.type.ts +0 -4
- package/src/logic-functions/types/removed-call-recorder-occurrence.type.ts +0 -6
- package/src/logic-functions/types/transcript-marker.type.ts +0 -6
- package/src/logic-functions/utils/as-record.util.ts +0 -6
- package/src/logic-functions/utils/get-application-variable-value.util.ts +0 -3
- package/src/logic-functions/utils/get-record-at-path.util.ts +0 -10
- package/src/logic-functions/utils/get-string.util.ts +0 -4
- package/src/logic-functions/utils/get-unique-sorted-ids.util.ts +0 -8
- package/src/logic-functions/utils/is-non-empty-string.util.ts +0 -5
- package/src/page-layouts/calendar-event-recording-tab.ts +0 -33
- package/src/view-fields/call-recorder-preference-on-calendar-event.view-field.ts +0 -27
- package/tsconfig.json +0 -42
- package/tsconfig.spec.json +0 -9
- package/vitest.config.ts +0 -31
- package/vitest.unit.config.ts +0 -14
|
@@ -0,0 +1,1602 @@
|
|
|
1
|
+
import { createRequire as __createRequire } from 'module';
|
|
2
|
+
const require = __createRequire(import.meta.url);
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __commonJS = (cb, mod) => function __require() {
|
|
10
|
+
try {
|
|
11
|
+
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
12
|
+
} catch (e) {
|
|
13
|
+
throw mod = 0, e;
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
var __copyProps = (to, from, except, desc) => {
|
|
17
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
18
|
+
for (let key of __getOwnPropNames(from))
|
|
19
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
20
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
21
|
+
}
|
|
22
|
+
return to;
|
|
23
|
+
};
|
|
24
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
25
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
26
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
27
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
28
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
29
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
30
|
+
mod
|
|
31
|
+
));
|
|
32
|
+
|
|
33
|
+
// node_modules/@sniptt/guards/build/guards/primitives.js
|
|
34
|
+
var require_primitives = __commonJS({
|
|
35
|
+
"node_modules/@sniptt/guards/build/guards/primitives.js"(exports) {
|
|
36
|
+
"use strict";
|
|
37
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
38
|
+
exports.isSymbol = exports.isBigInt = exports.isString = exports.isNumber = exports.isBoolean = exports.isUndefined = void 0;
|
|
39
|
+
var isUndefined17 = (term) => {
|
|
40
|
+
return typeof term === "undefined";
|
|
41
|
+
};
|
|
42
|
+
exports.isUndefined = isUndefined17;
|
|
43
|
+
var isBoolean = (term) => {
|
|
44
|
+
return typeof term === "boolean";
|
|
45
|
+
};
|
|
46
|
+
exports.isBoolean = isBoolean;
|
|
47
|
+
var isNumber = (term) => {
|
|
48
|
+
return typeof term === "number" && !Number.isNaN(term);
|
|
49
|
+
};
|
|
50
|
+
exports.isNumber = isNumber;
|
|
51
|
+
var isString5 = (term) => {
|
|
52
|
+
return typeof term === "string";
|
|
53
|
+
};
|
|
54
|
+
exports.isString = isString5;
|
|
55
|
+
var isBigInt = (term) => {
|
|
56
|
+
return typeof term === "bigint";
|
|
57
|
+
};
|
|
58
|
+
exports.isBigInt = isBigInt;
|
|
59
|
+
var isSymbol = (term) => {
|
|
60
|
+
return typeof term === "symbol";
|
|
61
|
+
};
|
|
62
|
+
exports.isSymbol = isSymbol;
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
// node_modules/@sniptt/guards/build/guards/structural.js
|
|
67
|
+
var require_structural = __commonJS({
|
|
68
|
+
"node_modules/@sniptt/guards/build/guards/structural.js"(exports) {
|
|
69
|
+
"use strict";
|
|
70
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
71
|
+
exports.isDate = exports.isWeakSet = exports.isWeakMap = exports.isSet = exports.isMap = exports.isArray = exports.isObject = exports.isFunction = exports.isNull = void 0;
|
|
72
|
+
var isNull = (term) => {
|
|
73
|
+
return term === null;
|
|
74
|
+
};
|
|
75
|
+
exports.isNull = isNull;
|
|
76
|
+
var isFunction = (term) => {
|
|
77
|
+
return typeof term === "function";
|
|
78
|
+
};
|
|
79
|
+
exports.isFunction = isFunction;
|
|
80
|
+
var isObject2 = (term) => {
|
|
81
|
+
return !exports.isNull(term) && typeof term === "object";
|
|
82
|
+
};
|
|
83
|
+
exports.isObject = isObject2;
|
|
84
|
+
var isArray2 = (term) => {
|
|
85
|
+
return Array.isArray(term);
|
|
86
|
+
};
|
|
87
|
+
exports.isArray = isArray2;
|
|
88
|
+
var isMap = (term) => {
|
|
89
|
+
return term instanceof Map;
|
|
90
|
+
};
|
|
91
|
+
exports.isMap = isMap;
|
|
92
|
+
var isSet = (term) => {
|
|
93
|
+
return term instanceof Set;
|
|
94
|
+
};
|
|
95
|
+
exports.isSet = isSet;
|
|
96
|
+
var isWeakMap = (term) => {
|
|
97
|
+
return term instanceof WeakMap;
|
|
98
|
+
};
|
|
99
|
+
exports.isWeakMap = isWeakMap;
|
|
100
|
+
var isWeakSet = (term) => {
|
|
101
|
+
return term instanceof WeakSet;
|
|
102
|
+
};
|
|
103
|
+
exports.isWeakSet = isWeakSet;
|
|
104
|
+
var isDate = (term) => {
|
|
105
|
+
return term instanceof Date;
|
|
106
|
+
};
|
|
107
|
+
exports.isDate = isDate;
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
// node_modules/@sniptt/guards/build/guards/convenience.js
|
|
112
|
+
var require_convenience = __commonJS({
|
|
113
|
+
"node_modules/@sniptt/guards/build/guards/convenience.js"(exports) {
|
|
114
|
+
"use strict";
|
|
115
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
116
|
+
exports.isNegativeInteger = exports.isNonNegativeInteger = exports.isPositiveInteger = exports.isInteger = exports.isNumberOrNaN = exports.isNonEmptyString = exports.isNonEmptyArray = exports.isObjectOrNull = void 0;
|
|
117
|
+
var primitives_1 = require_primitives();
|
|
118
|
+
var structural_1 = require_structural();
|
|
119
|
+
var isObjectOrNull = (term) => {
|
|
120
|
+
return typeof term === "object";
|
|
121
|
+
};
|
|
122
|
+
exports.isObjectOrNull = isObjectOrNull;
|
|
123
|
+
var isNonEmptyArray = (term) => {
|
|
124
|
+
return structural_1.isArray(term) && term.length > 0;
|
|
125
|
+
};
|
|
126
|
+
exports.isNonEmptyArray = isNonEmptyArray;
|
|
127
|
+
var isNonEmptyString2 = (term) => {
|
|
128
|
+
return primitives_1.isString(term) && term.length > 0;
|
|
129
|
+
};
|
|
130
|
+
exports.isNonEmptyString = isNonEmptyString2;
|
|
131
|
+
var isNumberOrNaN = (term) => {
|
|
132
|
+
return typeof term === "number";
|
|
133
|
+
};
|
|
134
|
+
exports.isNumberOrNaN = isNumberOrNaN;
|
|
135
|
+
var isInteger = (term) => {
|
|
136
|
+
return primitives_1.isNumber(term) && Number.isInteger(term);
|
|
137
|
+
};
|
|
138
|
+
exports.isInteger = isInteger;
|
|
139
|
+
var isPositiveInteger = (term) => {
|
|
140
|
+
return exports.isInteger(term) && term > 0;
|
|
141
|
+
};
|
|
142
|
+
exports.isPositiveInteger = isPositiveInteger;
|
|
143
|
+
var isNonNegativeInteger = (term) => {
|
|
144
|
+
return exports.isInteger(term) && term >= 0;
|
|
145
|
+
};
|
|
146
|
+
exports.isNonNegativeInteger = isNonNegativeInteger;
|
|
147
|
+
var isNegativeInteger = (term) => {
|
|
148
|
+
return exports.isInteger(term) && term < 0;
|
|
149
|
+
};
|
|
150
|
+
exports.isNegativeInteger = isNegativeInteger;
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
// node_modules/@sniptt/guards/build/index.js
|
|
155
|
+
var require_build = __commonJS({
|
|
156
|
+
"node_modules/@sniptt/guards/build/index.js"(exports) {
|
|
157
|
+
"use strict";
|
|
158
|
+
var __createBinding = exports && exports.__createBinding || (Object.create ? (function(o, m, k, k2) {
|
|
159
|
+
if (k2 === void 0) k2 = k;
|
|
160
|
+
Object.defineProperty(o, k2, { enumerable: true, get: function() {
|
|
161
|
+
return m[k];
|
|
162
|
+
} });
|
|
163
|
+
}) : (function(o, m, k, k2) {
|
|
164
|
+
if (k2 === void 0) k2 = k;
|
|
165
|
+
o[k2] = m[k];
|
|
166
|
+
}));
|
|
167
|
+
var __setModuleDefault = exports && exports.__setModuleDefault || (Object.create ? (function(o, v) {
|
|
168
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
169
|
+
}) : function(o, v) {
|
|
170
|
+
o["default"] = v;
|
|
171
|
+
});
|
|
172
|
+
var __importStar = exports && exports.__importStar || function(mod) {
|
|
173
|
+
if (mod && mod.__esModule) return mod;
|
|
174
|
+
var result = {};
|
|
175
|
+
if (mod != null) {
|
|
176
|
+
for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
177
|
+
}
|
|
178
|
+
__setModuleDefault(result, mod);
|
|
179
|
+
return result;
|
|
180
|
+
};
|
|
181
|
+
var __exportStar = exports && exports.__exportStar || function(m, exports2) {
|
|
182
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports2, p)) __createBinding(exports2, m, p);
|
|
183
|
+
};
|
|
184
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
185
|
+
exports.structural = exports.primitives = exports.convenience = void 0;
|
|
186
|
+
exports.convenience = __importStar(require_convenience());
|
|
187
|
+
__exportStar(require_convenience(), exports);
|
|
188
|
+
exports.primitives = __importStar(require_primitives());
|
|
189
|
+
__exportStar(require_primitives(), exports);
|
|
190
|
+
exports.structural = __importStar(require_structural());
|
|
191
|
+
__exportStar(require_structural(), exports);
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
// src/logic-functions/reconcile-call-recorder-calendar-event.ts
|
|
196
|
+
var import_guards19 = __toESM(require_build());
|
|
197
|
+
import { CoreApiClient } from "twenty-client-sdk/core";
|
|
198
|
+
|
|
199
|
+
// twenty-sdk-define-stub:__twenty-sdk-define-stub__
|
|
200
|
+
var __defineFactoryStub = (config) => ({
|
|
201
|
+
success: true,
|
|
202
|
+
config,
|
|
203
|
+
errors: []
|
|
204
|
+
});
|
|
205
|
+
var __anyHandler = {
|
|
206
|
+
get(_target, prop) {
|
|
207
|
+
if (prop === "__esModule") return true;
|
|
208
|
+
if (prop === Symbol.toPrimitive) return () => "";
|
|
209
|
+
if (typeof prop === "symbol") return void 0;
|
|
210
|
+
return new Proxy(() => void 0, __anyHandler);
|
|
211
|
+
},
|
|
212
|
+
apply() {
|
|
213
|
+
return new Proxy(() => void 0, __anyHandler);
|
|
214
|
+
}
|
|
215
|
+
};
|
|
216
|
+
var __anyStub = new Proxy(() => void 0, __anyHandler);
|
|
217
|
+
var defineLogicFunction = __defineFactoryStub;
|
|
218
|
+
|
|
219
|
+
// src/constants/calendar-event-reconciliation-logic-function-universal-identifier.ts
|
|
220
|
+
var CALENDAR_EVENT_RECONCILIATION_LOGIC_FUNCTION_UNIVERSAL_IDENTIFIER = "1f28c477-6423-4911-85bf-2296ef112be9";
|
|
221
|
+
|
|
222
|
+
// src/logic-functions/domain/compute-real-meeting-key.util.ts
|
|
223
|
+
var import_guards2 = __toESM(require_build());
|
|
224
|
+
|
|
225
|
+
// src/logic-functions/utils/is-non-empty-string.util.ts
|
|
226
|
+
var import_guards = __toESM(require_build());
|
|
227
|
+
var isNonEmptyString = (value) => (0, import_guards.isString)(value) && value.trim() !== "";
|
|
228
|
+
|
|
229
|
+
// src/logic-functions/domain/compute-real-meeting-key.util.ts
|
|
230
|
+
var computeRealMeetingKey = ({
|
|
231
|
+
calendarEventId,
|
|
232
|
+
conferenceLinkUrl,
|
|
233
|
+
iCalUid,
|
|
234
|
+
startsAt
|
|
235
|
+
}) => {
|
|
236
|
+
const normalizedConferenceLink = normalizeConferenceLink(conferenceLinkUrl);
|
|
237
|
+
if (!(0, import_guards2.isUndefined)(normalizedConferenceLink)) {
|
|
238
|
+
return `link:${normalizedConferenceLink}:${startsAt ?? ""}`;
|
|
239
|
+
}
|
|
240
|
+
if (isNonEmptyString(iCalUid)) {
|
|
241
|
+
return `ical:${iCalUid}:${startsAt ?? ""}`;
|
|
242
|
+
}
|
|
243
|
+
return `event:${calendarEventId}`;
|
|
244
|
+
};
|
|
245
|
+
var normalizeConferenceLink = (conferenceLinkUrl) => {
|
|
246
|
+
if (!isNonEmptyString(conferenceLinkUrl)) {
|
|
247
|
+
return void 0;
|
|
248
|
+
}
|
|
249
|
+
const withoutProtocol = conferenceLinkUrl.trim().toLowerCase().replace(/^https?:\/\//, "").replace(/^www\./, "");
|
|
250
|
+
const withoutQueryAndFragment = withoutProtocol.split(/[?#]/)[0];
|
|
251
|
+
const withoutTrailingSlash = withoutQueryAndFragment.replace(/\/+$/, "");
|
|
252
|
+
return withoutTrailingSlash === "" ? void 0 : withoutTrailingSlash;
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
// src/logic-functions/utils/get-unique-sorted-ids.util.ts
|
|
256
|
+
var import_guards3 = __toESM(require_build());
|
|
257
|
+
var getUniqueSortedIds = (ids) => [...new Set(ids.filter(import_guards3.isString))].sort(
|
|
258
|
+
(firstId, secondId) => firstId.localeCompare(secondId)
|
|
259
|
+
);
|
|
260
|
+
|
|
261
|
+
// src/logic-functions/flows/reconcile-call-recorder.util.ts
|
|
262
|
+
var import_guards18 = __toESM(require_build());
|
|
263
|
+
|
|
264
|
+
// src/logic-functions/domain/aggregate-call-recorder-policy-results-by-meeting.util.ts
|
|
265
|
+
var aggregateCallRecorderPolicyResultsByMeeting = (perCalendarEventPolicyResults) => {
|
|
266
|
+
const meetingPolicyResultsByMeetingKey = /* @__PURE__ */ new Map();
|
|
267
|
+
for (const {
|
|
268
|
+
calendarEventId,
|
|
269
|
+
realMeetingKey,
|
|
270
|
+
shouldRequestBot
|
|
271
|
+
} of perCalendarEventPolicyResults) {
|
|
272
|
+
const meetingPolicyResult = meetingPolicyResultsByMeetingKey.get(
|
|
273
|
+
realMeetingKey
|
|
274
|
+
) ?? {
|
|
275
|
+
realMeetingKey,
|
|
276
|
+
shouldRequestBot: false,
|
|
277
|
+
calendarEventIds: [],
|
|
278
|
+
requestingCalendarEventIds: []
|
|
279
|
+
};
|
|
280
|
+
meetingPolicyResult.calendarEventIds.push(calendarEventId);
|
|
281
|
+
if (shouldRequestBot) {
|
|
282
|
+
meetingPolicyResult.shouldRequestBot = true;
|
|
283
|
+
meetingPolicyResult.requestingCalendarEventIds.push(calendarEventId);
|
|
284
|
+
}
|
|
285
|
+
meetingPolicyResultsByMeetingKey.set(realMeetingKey, meetingPolicyResult);
|
|
286
|
+
}
|
|
287
|
+
return [...meetingPolicyResultsByMeetingKey.values()];
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
// src/constants/call-recorder-preference.ts
|
|
291
|
+
var CallRecorderPreference = /* @__PURE__ */ ((CallRecorderPreference2) => {
|
|
292
|
+
CallRecorderPreference2["ON"] = "ON";
|
|
293
|
+
CallRecorderPreference2["OFF"] = "OFF";
|
|
294
|
+
return CallRecorderPreference2;
|
|
295
|
+
})(CallRecorderPreference || {});
|
|
296
|
+
|
|
297
|
+
// src/logic-functions/domain/resolve-call-recorder-policy-result.util.ts
|
|
298
|
+
var resolveCallRecorderPolicyResult = ({
|
|
299
|
+
input,
|
|
300
|
+
now
|
|
301
|
+
}) => {
|
|
302
|
+
if (input.isCanceled) {
|
|
303
|
+
return botNotRequired("EVENT_CANCELED");
|
|
304
|
+
}
|
|
305
|
+
if (input.callRecorderPreference === "OFF" /* OFF */) {
|
|
306
|
+
return botNotRequired("PREFERENCE_OFF");
|
|
307
|
+
}
|
|
308
|
+
if (!isNonEmptyString(input.conferenceLinkUrl)) {
|
|
309
|
+
return botNotRequired("MISSING_CONFERENCE_LINK");
|
|
310
|
+
}
|
|
311
|
+
if (!isCalendarEventInFuture({
|
|
312
|
+
startsAt: input.startsAt,
|
|
313
|
+
endsAt: input.endsAt,
|
|
314
|
+
now
|
|
315
|
+
})) {
|
|
316
|
+
return botNotRequired("EVENT_NOT_UPCOMING");
|
|
317
|
+
}
|
|
318
|
+
return botRequired("RECORDING_ENABLED");
|
|
319
|
+
};
|
|
320
|
+
var isCalendarEventInFuture = ({
|
|
321
|
+
startsAt,
|
|
322
|
+
endsAt,
|
|
323
|
+
now
|
|
324
|
+
}) => {
|
|
325
|
+
const reference = endsAt ?? startsAt;
|
|
326
|
+
if (!isNonEmptyString(reference)) {
|
|
327
|
+
return false;
|
|
328
|
+
}
|
|
329
|
+
const referenceTime = new Date(reference).getTime();
|
|
330
|
+
if (Number.isNaN(referenceTime)) {
|
|
331
|
+
return false;
|
|
332
|
+
}
|
|
333
|
+
return referenceTime > now.getTime();
|
|
334
|
+
};
|
|
335
|
+
var botRequired = (reason) => ({ shouldRequestBot: true, reason });
|
|
336
|
+
var botNotRequired = (reason) => ({ shouldRequestBot: false, reason });
|
|
337
|
+
|
|
338
|
+
// src/logic-functions/domain/build-call-recorder-policy-result.util.ts
|
|
339
|
+
var buildCallRecorderPolicyResult = (calendarEvent, now) => {
|
|
340
|
+
const realMeetingKey = computeRealMeetingKey({
|
|
341
|
+
calendarEventId: calendarEvent.id,
|
|
342
|
+
conferenceLinkUrl: calendarEvent.conferenceLinkUrl,
|
|
343
|
+
iCalUid: calendarEvent.iCalUid,
|
|
344
|
+
startsAt: calendarEvent.startsAt
|
|
345
|
+
});
|
|
346
|
+
const callRecorderPreference = normalizeCallRecorderPreference(
|
|
347
|
+
calendarEvent.callRecorderPreference
|
|
348
|
+
);
|
|
349
|
+
const policyResult = resolveCallRecorderPolicyResult({
|
|
350
|
+
input: {
|
|
351
|
+
callRecorderPreference,
|
|
352
|
+
isCanceled: calendarEvent.isCanceled,
|
|
353
|
+
startsAt: calendarEvent.startsAt,
|
|
354
|
+
endsAt: calendarEvent.endsAt,
|
|
355
|
+
conferenceLinkUrl: calendarEvent.conferenceLinkUrl
|
|
356
|
+
},
|
|
357
|
+
now
|
|
358
|
+
});
|
|
359
|
+
return {
|
|
360
|
+
calendarEventId: calendarEvent.id,
|
|
361
|
+
callRecorderPreference,
|
|
362
|
+
realMeetingKey,
|
|
363
|
+
...policyResult
|
|
364
|
+
};
|
|
365
|
+
};
|
|
366
|
+
var normalizeCallRecorderPreference = (callRecorderPreference) => isCallRecorderPreference(callRecorderPreference) ? callRecorderPreference : void 0;
|
|
367
|
+
var isCallRecorderPreference = (callRecorderPreference) => Object.values(CallRecorderPreference).some(
|
|
368
|
+
(preference) => preference === callRecorderPreference
|
|
369
|
+
);
|
|
370
|
+
|
|
371
|
+
// src/logic-functions/flows/cancel-call-recording-request.util.ts
|
|
372
|
+
var import_guards6 = __toESM(require_build());
|
|
373
|
+
|
|
374
|
+
// src/logic-functions/recall-api/get-recall-api-config.util.ts
|
|
375
|
+
var import_guards4 = __toESM(require_build());
|
|
376
|
+
|
|
377
|
+
// src/logic-functions/constants/call-recorder-name-env-var-name.ts
|
|
378
|
+
var CALL_RECORDER_NAME_ENV_VAR_NAME = "CALL_RECORDER_NAME";
|
|
379
|
+
|
|
380
|
+
// src/logic-functions/constants/default-call-recorder-name.ts
|
|
381
|
+
var DEFAULT_CALL_RECORDER_NAME = "Twenty.com";
|
|
382
|
+
|
|
383
|
+
// src/logic-functions/constants/default-recall-region.ts
|
|
384
|
+
var DEFAULT_RECALL_REGION = "eu-central-1";
|
|
385
|
+
|
|
386
|
+
// src/logic-functions/constants/recall-api-key-env-var-name.ts
|
|
387
|
+
var RECALL_API_KEY_ENV_VAR_NAME = "RECALL_API_KEY";
|
|
388
|
+
|
|
389
|
+
// src/logic-functions/constants/recall-region-env-var-name.ts
|
|
390
|
+
var RECALL_REGION_ENV_VAR_NAME = "RECALL_REGION";
|
|
391
|
+
|
|
392
|
+
// src/logic-functions/utils/get-application-variable-value.util.ts
|
|
393
|
+
var getApplicationVariableValue = (key) => process.env[key];
|
|
394
|
+
|
|
395
|
+
// src/logic-functions/recall-api/get-recall-api-config.util.ts
|
|
396
|
+
var getRecallApiConfig = () => {
|
|
397
|
+
const apiKey = normalizeOptionalString(
|
|
398
|
+
getApplicationVariableValue(RECALL_API_KEY_ENV_VAR_NAME)
|
|
399
|
+
);
|
|
400
|
+
if ((0, import_guards4.isUndefined)(apiKey)) {
|
|
401
|
+
return {
|
|
402
|
+
success: false,
|
|
403
|
+
error: "RECALL_API_KEY server variable is not set. A server admin must set it on the Call Recorder application registration before scheduling bots."
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
const region = normalizeOptionalString(
|
|
407
|
+
getApplicationVariableValue(RECALL_REGION_ENV_VAR_NAME)
|
|
408
|
+
) ?? DEFAULT_RECALL_REGION;
|
|
409
|
+
const botName = normalizeOptionalString(
|
|
410
|
+
getApplicationVariableValue(CALL_RECORDER_NAME_ENV_VAR_NAME)
|
|
411
|
+
) ?? DEFAULT_CALL_RECORDER_NAME;
|
|
412
|
+
return {
|
|
413
|
+
success: true,
|
|
414
|
+
config: {
|
|
415
|
+
apiKey,
|
|
416
|
+
baseUrl: `https://${region}.recall.ai/api/v1`,
|
|
417
|
+
botName
|
|
418
|
+
}
|
|
419
|
+
};
|
|
420
|
+
};
|
|
421
|
+
var normalizeOptionalString = (value) => isNonEmptyString(value) ? value.trim() : void 0;
|
|
422
|
+
|
|
423
|
+
// src/logic-functions/recall-api/recall-bot-api-request.util.ts
|
|
424
|
+
var import_guards5 = __toESM(require_build());
|
|
425
|
+
|
|
426
|
+
// src/logic-functions/constants/recall-api-max-attempts.ts
|
|
427
|
+
var RECALL_API_MAX_ATTEMPTS = 3;
|
|
428
|
+
|
|
429
|
+
// src/logic-functions/constants/recall-api-retry-delay-ms.ts
|
|
430
|
+
var RECALL_API_RETRY_DELAY_MS = 500;
|
|
431
|
+
|
|
432
|
+
// src/logic-functions/recall-api/recall-bot-api-request.util.ts
|
|
433
|
+
var recallBotApiRequest = async (requestArgs) => {
|
|
434
|
+
const maxAttempts = requestArgs.maxAttempts ?? RECALL_API_MAX_ATTEMPTS;
|
|
435
|
+
for (let attemptNumber = 1; ; attemptNumber++) {
|
|
436
|
+
const { result, isRetryable } = await performRecallBotApiRequestAttempt(requestArgs);
|
|
437
|
+
if (!isRetryable || attemptNumber >= maxAttempts) {
|
|
438
|
+
return result;
|
|
439
|
+
}
|
|
440
|
+
await sleep(RECALL_API_RETRY_DELAY_MS * attemptNumber);
|
|
441
|
+
}
|
|
442
|
+
};
|
|
443
|
+
var performRecallBotApiRequestAttempt = async ({
|
|
444
|
+
config,
|
|
445
|
+
path,
|
|
446
|
+
method,
|
|
447
|
+
body,
|
|
448
|
+
allowNotFound = false
|
|
449
|
+
}) => {
|
|
450
|
+
let response;
|
|
451
|
+
try {
|
|
452
|
+
response = await fetch(`${config.baseUrl}${path}`, {
|
|
453
|
+
method,
|
|
454
|
+
headers: {
|
|
455
|
+
Authorization: buildRecallApiAuthorizationHeader(config.apiKey),
|
|
456
|
+
...(0, import_guards5.isUndefined)(body) ? {} : { "Content-Type": "application/json" }
|
|
457
|
+
},
|
|
458
|
+
...(0, import_guards5.isUndefined)(body) ? {} : { body: JSON.stringify(body) }
|
|
459
|
+
});
|
|
460
|
+
} catch (error) {
|
|
461
|
+
return {
|
|
462
|
+
isRetryable: true,
|
|
463
|
+
result: {
|
|
464
|
+
ok: false,
|
|
465
|
+
status: null,
|
|
466
|
+
errorMessage: `Recall API request failed: ${error instanceof Error ? error.message : String(error)}`
|
|
467
|
+
}
|
|
468
|
+
};
|
|
469
|
+
}
|
|
470
|
+
if (allowNotFound && response.status === 404) {
|
|
471
|
+
return {
|
|
472
|
+
isRetryable: false,
|
|
473
|
+
result: {
|
|
474
|
+
ok: true,
|
|
475
|
+
status: response.status,
|
|
476
|
+
data: void 0
|
|
477
|
+
}
|
|
478
|
+
};
|
|
479
|
+
}
|
|
480
|
+
if (response.status === 204) {
|
|
481
|
+
return {
|
|
482
|
+
isRetryable: false,
|
|
483
|
+
result: {
|
|
484
|
+
ok: true,
|
|
485
|
+
status: response.status,
|
|
486
|
+
data: void 0
|
|
487
|
+
}
|
|
488
|
+
};
|
|
489
|
+
}
|
|
490
|
+
if (!response.ok) {
|
|
491
|
+
return {
|
|
492
|
+
isRetryable: isRetryableRecallApiStatus(response.status),
|
|
493
|
+
result: {
|
|
494
|
+
ok: false,
|
|
495
|
+
status: response.status,
|
|
496
|
+
errorMessage: await extractRecallApiErrorMessage(response)
|
|
497
|
+
}
|
|
498
|
+
};
|
|
499
|
+
}
|
|
500
|
+
try {
|
|
501
|
+
return {
|
|
502
|
+
isRetryable: false,
|
|
503
|
+
result: {
|
|
504
|
+
ok: true,
|
|
505
|
+
status: response.status,
|
|
506
|
+
data: await response.json()
|
|
507
|
+
}
|
|
508
|
+
};
|
|
509
|
+
} catch (error) {
|
|
510
|
+
return {
|
|
511
|
+
isRetryable: false,
|
|
512
|
+
result: {
|
|
513
|
+
ok: false,
|
|
514
|
+
status: response.status,
|
|
515
|
+
errorMessage: `Recall API returned a non-JSON response: ${error instanceof Error ? error.message : String(error)}`
|
|
516
|
+
}
|
|
517
|
+
};
|
|
518
|
+
}
|
|
519
|
+
};
|
|
520
|
+
var isRetryableRecallApiStatus = (status) => status === 429 || status >= 500;
|
|
521
|
+
var sleep = (delayMs) => new Promise((resolve) => {
|
|
522
|
+
setTimeout(resolve, delayMs);
|
|
523
|
+
});
|
|
524
|
+
var buildRecallApiAuthorizationHeader = (apiKey) => {
|
|
525
|
+
const trimmedApiKey = apiKey.trim();
|
|
526
|
+
return trimmedApiKey.toLowerCase().startsWith("token ") ? trimmedApiKey : `Token ${trimmedApiKey}`;
|
|
527
|
+
};
|
|
528
|
+
var extractRecallApiErrorMessage = async (response) => {
|
|
529
|
+
const fallback = `Recall API responded with HTTP ${response.status}`;
|
|
530
|
+
try {
|
|
531
|
+
const body = await response.json();
|
|
532
|
+
return `${fallback}: ${JSON.stringify(body)}`;
|
|
533
|
+
} catch {
|
|
534
|
+
return fallback;
|
|
535
|
+
}
|
|
536
|
+
};
|
|
537
|
+
|
|
538
|
+
// src/logic-functions/recall-api/cancel-recall-bot.util.ts
|
|
539
|
+
var cancelRecallBot = async ({
|
|
540
|
+
externalBotId
|
|
541
|
+
}) => {
|
|
542
|
+
const configResult = getRecallApiConfig();
|
|
543
|
+
if (!configResult.success) {
|
|
544
|
+
return { ok: false, status: null, errorMessage: configResult.error };
|
|
545
|
+
}
|
|
546
|
+
const result = await recallBotApiRequest({
|
|
547
|
+
config: configResult.config,
|
|
548
|
+
path: `/bot/${externalBotId}/`,
|
|
549
|
+
method: "DELETE",
|
|
550
|
+
allowNotFound: true
|
|
551
|
+
});
|
|
552
|
+
if (!result.ok) {
|
|
553
|
+
return result;
|
|
554
|
+
}
|
|
555
|
+
return { ok: true };
|
|
556
|
+
};
|
|
557
|
+
|
|
558
|
+
// src/logic-functions/data/update-call-recording.util.ts
|
|
559
|
+
var updateCallRecording = async (client, {
|
|
560
|
+
id,
|
|
561
|
+
data
|
|
562
|
+
}) => {
|
|
563
|
+
await client.mutation({
|
|
564
|
+
updateCallRecording: {
|
|
565
|
+
__args: {
|
|
566
|
+
id,
|
|
567
|
+
data
|
|
568
|
+
},
|
|
569
|
+
id: true
|
|
570
|
+
}
|
|
571
|
+
});
|
|
572
|
+
};
|
|
573
|
+
|
|
574
|
+
// src/logic-functions/flows/cancel-call-recording-request.util.ts
|
|
575
|
+
var cancelCallRecordingRequest = async ({
|
|
576
|
+
client,
|
|
577
|
+
callRecording
|
|
578
|
+
}) => {
|
|
579
|
+
await updateCallRecording(client, {
|
|
580
|
+
id: callRecording.id,
|
|
581
|
+
data: {
|
|
582
|
+
recordingRequestStatus: "CANCELED" /* CANCELED */
|
|
583
|
+
}
|
|
584
|
+
});
|
|
585
|
+
if ((0, import_guards6.isUndefined)(callRecording.externalBotId)) {
|
|
586
|
+
return;
|
|
587
|
+
}
|
|
588
|
+
const cancelResult = await cancelRecallBot({
|
|
589
|
+
externalBotId: callRecording.externalBotId
|
|
590
|
+
});
|
|
591
|
+
if (!cancelResult.ok) {
|
|
592
|
+
console.warn(
|
|
593
|
+
`[call-recorder] failed to cancel Recall bot for callRecording ${callRecording.id}, leaving it for the stale-state cron: ${cancelResult.errorMessage}`
|
|
594
|
+
);
|
|
595
|
+
return;
|
|
596
|
+
}
|
|
597
|
+
await updateCallRecording(client, {
|
|
598
|
+
id: callRecording.id,
|
|
599
|
+
data: {
|
|
600
|
+
externalBotId: null
|
|
601
|
+
}
|
|
602
|
+
});
|
|
603
|
+
};
|
|
604
|
+
|
|
605
|
+
// src/logic-functions/domain/compute-call-recording-id-for-meeting.util.ts
|
|
606
|
+
import { createHash } from "crypto";
|
|
607
|
+
var computeCallRecordingIdForMeeting = (realMeetingKey) => {
|
|
608
|
+
const bytes = createHash("sha256").update(realMeetingKey).digest();
|
|
609
|
+
bytes[6] = bytes[6] & 15 | 64;
|
|
610
|
+
bytes[8] = bytes[8] & 63 | 128;
|
|
611
|
+
const hex = bytes.subarray(0, 16).toString("hex");
|
|
612
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
613
|
+
};
|
|
614
|
+
|
|
615
|
+
// src/logic-functions/data/create-call-recording.util.ts
|
|
616
|
+
var import_guards7 = __toESM(require_build());
|
|
617
|
+
var createCallRecording = async (client, {
|
|
618
|
+
id,
|
|
619
|
+
data
|
|
620
|
+
}) => {
|
|
621
|
+
const mutationResult = await client.mutation({
|
|
622
|
+
createCallRecording: {
|
|
623
|
+
__args: {
|
|
624
|
+
data: { id, ...data }
|
|
625
|
+
},
|
|
626
|
+
id: true
|
|
627
|
+
}
|
|
628
|
+
});
|
|
629
|
+
const createdCallRecordingId = mutationResult.createCallRecording?.id;
|
|
630
|
+
if ((0, import_guards7.isUndefined)(createdCallRecordingId)) {
|
|
631
|
+
throw new Error(
|
|
632
|
+
"createCallRecording mutation did not return a call recording id"
|
|
633
|
+
);
|
|
634
|
+
}
|
|
635
|
+
return createdCallRecordingId;
|
|
636
|
+
};
|
|
637
|
+
|
|
638
|
+
// src/logic-functions/flows/ensure-call-recorder.util.ts
|
|
639
|
+
var import_guards14 = __toESM(require_build());
|
|
640
|
+
|
|
641
|
+
// src/logic-functions/domain/build-recall-routing-metadata.util.ts
|
|
642
|
+
var buildRecallRoutingMetadata = ({
|
|
643
|
+
callRecordingId,
|
|
644
|
+
workspaceId
|
|
645
|
+
}) => ({
|
|
646
|
+
twentyWorkspaceId: workspaceId,
|
|
647
|
+
twentyCallRecordingId: callRecordingId
|
|
648
|
+
});
|
|
649
|
+
|
|
650
|
+
// src/logic-functions/constants/default-call-recorder-join-early-minutes.ts
|
|
651
|
+
var DEFAULT_CALL_RECORDER_JOIN_EARLY_MINUTES = 1;
|
|
652
|
+
|
|
653
|
+
// src/logic-functions/constants/milliseconds-per-minute.ts
|
|
654
|
+
var MILLISECONDS_PER_MINUTE = 6e4;
|
|
655
|
+
|
|
656
|
+
// src/logic-functions/constants/call-recorder-join-early-minutes-env-var-name.ts
|
|
657
|
+
var CALL_RECORDER_JOIN_EARLY_MINUTES_ENV_VAR_NAME = "CALL_RECORDER_JOIN_EARLY_MINUTES";
|
|
658
|
+
|
|
659
|
+
// src/logic-functions/domain/compute-recall-bot-join-at.util.ts
|
|
660
|
+
var computeRecallBotJoinAt = (meetingStartsAt) => {
|
|
661
|
+
const meetingStartTimeInMilliseconds = new Date(meetingStartsAt).getTime();
|
|
662
|
+
if (Number.isNaN(meetingStartTimeInMilliseconds)) {
|
|
663
|
+
return meetingStartsAt;
|
|
664
|
+
}
|
|
665
|
+
return new Date(
|
|
666
|
+
meetingStartTimeInMilliseconds - getRecallBotJoinEarlyMinutes() * MILLISECONDS_PER_MINUTE
|
|
667
|
+
).toISOString();
|
|
668
|
+
};
|
|
669
|
+
var getRecallBotJoinEarlyMinutes = () => {
|
|
670
|
+
const rawValue = getApplicationVariableValue(
|
|
671
|
+
CALL_RECORDER_JOIN_EARLY_MINUTES_ENV_VAR_NAME
|
|
672
|
+
);
|
|
673
|
+
if (!isNonEmptyString(rawValue)) {
|
|
674
|
+
return DEFAULT_CALL_RECORDER_JOIN_EARLY_MINUTES;
|
|
675
|
+
}
|
|
676
|
+
const joinEarlyMinutes = Number(rawValue.trim());
|
|
677
|
+
return Number.isInteger(joinEarlyMinutes) && joinEarlyMinutes >= 0 ? joinEarlyMinutes : DEFAULT_CALL_RECORDER_JOIN_EARLY_MINUTES;
|
|
678
|
+
};
|
|
679
|
+
|
|
680
|
+
// src/logic-functions/data/find-call-recordings-by-filter.util.ts
|
|
681
|
+
var import_guards9 = __toESM(require_build());
|
|
682
|
+
|
|
683
|
+
// src/logic-functions/constants/twenty-page-size.ts
|
|
684
|
+
var TWENTY_PAGE_SIZE = 100;
|
|
685
|
+
|
|
686
|
+
// src/logic-functions/data/fetch-all-nodes.util.ts
|
|
687
|
+
var import_guards8 = __toESM(require_build());
|
|
688
|
+
var fetchAllNodes = async (fetchPage) => {
|
|
689
|
+
const nodes = [];
|
|
690
|
+
let hasNextPage = true;
|
|
691
|
+
let afterCursor;
|
|
692
|
+
while (hasNextPage) {
|
|
693
|
+
const connection = await fetchPage(afterCursor);
|
|
694
|
+
if ((0, import_guards8.isUndefined)(connection)) {
|
|
695
|
+
throw new Error("Pagination query returned no connection");
|
|
696
|
+
}
|
|
697
|
+
for (const edge of connection.edges ?? []) {
|
|
698
|
+
nodes.push(edge.node);
|
|
699
|
+
}
|
|
700
|
+
hasNextPage = connection.pageInfo?.hasNextPage === true;
|
|
701
|
+
const endCursor = connection.pageInfo?.endCursor;
|
|
702
|
+
if (hasNextPage && !(0, import_guards8.isString)(endCursor)) {
|
|
703
|
+
throw new Error(
|
|
704
|
+
"Inconsistent pagination state: hasNextPage is true without an endCursor"
|
|
705
|
+
);
|
|
706
|
+
}
|
|
707
|
+
afterCursor = (0, import_guards8.isString)(endCursor) ? endCursor : void 0;
|
|
708
|
+
}
|
|
709
|
+
return nodes;
|
|
710
|
+
};
|
|
711
|
+
|
|
712
|
+
// src/logic-functions/data/find-call-recordings-by-filter.util.ts
|
|
713
|
+
var findCallRecordingsByFilter = async (client, filter) => {
|
|
714
|
+
const callRecordingNodes = await fetchAllNodes(
|
|
715
|
+
async (afterCursor) => {
|
|
716
|
+
const queryResult = await client.query({
|
|
717
|
+
callRecordings: {
|
|
718
|
+
__args: {
|
|
719
|
+
filter,
|
|
720
|
+
first: TWENTY_PAGE_SIZE,
|
|
721
|
+
...(0, import_guards9.isUndefined)(afterCursor) ? {} : { after: afterCursor }
|
|
722
|
+
},
|
|
723
|
+
pageInfo: {
|
|
724
|
+
hasNextPage: true,
|
|
725
|
+
endCursor: true
|
|
726
|
+
},
|
|
727
|
+
edges: {
|
|
728
|
+
node: {
|
|
729
|
+
id: true,
|
|
730
|
+
title: true,
|
|
731
|
+
status: true,
|
|
732
|
+
recordingRequestStatus: true,
|
|
733
|
+
startedAt: true,
|
|
734
|
+
endedAt: true,
|
|
735
|
+
calendarEventId: true,
|
|
736
|
+
externalBotId: true,
|
|
737
|
+
externalRecordingId: true,
|
|
738
|
+
callRecorderFailureReason: true
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
});
|
|
743
|
+
return queryResult.callRecordings;
|
|
744
|
+
}
|
|
745
|
+
);
|
|
746
|
+
return callRecordingNodes.map((callRecording) => ({
|
|
747
|
+
id: callRecording.id,
|
|
748
|
+
title: callRecording.title ?? void 0,
|
|
749
|
+
status: callRecording.status ?? void 0,
|
|
750
|
+
recordingRequestStatus: normalizeCallRecordingRequestStatus(
|
|
751
|
+
callRecording.recordingRequestStatus
|
|
752
|
+
),
|
|
753
|
+
startedAt: callRecording.startedAt ?? void 0,
|
|
754
|
+
endedAt: callRecording.endedAt ?? void 0,
|
|
755
|
+
calendarEventId: callRecording.calendarEventId ?? void 0,
|
|
756
|
+
externalBotId: normalizeOptionalString2(callRecording.externalBotId),
|
|
757
|
+
externalRecordingId: normalizeOptionalString2(
|
|
758
|
+
callRecording.externalRecordingId
|
|
759
|
+
),
|
|
760
|
+
callRecorderFailureReason: normalizeOptionalString2(
|
|
761
|
+
callRecording.callRecorderFailureReason
|
|
762
|
+
)
|
|
763
|
+
}));
|
|
764
|
+
};
|
|
765
|
+
var normalizeOptionalString2 = (value) => isNonEmptyString(value) ? value : void 0;
|
|
766
|
+
var normalizeCallRecordingRequestStatus = (recordingRequestStatus) => {
|
|
767
|
+
if (recordingRequestStatus === "REQUESTED" /* REQUESTED */) {
|
|
768
|
+
return recordingRequestStatus;
|
|
769
|
+
}
|
|
770
|
+
if (recordingRequestStatus === "CANCELED" /* CANCELED */) {
|
|
771
|
+
return recordingRequestStatus;
|
|
772
|
+
}
|
|
773
|
+
return void 0;
|
|
774
|
+
};
|
|
775
|
+
|
|
776
|
+
// src/logic-functions/data/find-call-recordings-by-ids.util.ts
|
|
777
|
+
var findCallRecordingsByIds = async (client, callRecordingIds) => {
|
|
778
|
+
if (callRecordingIds.length === 0) {
|
|
779
|
+
return [];
|
|
780
|
+
}
|
|
781
|
+
return findCallRecordingsByFilter(client, {
|
|
782
|
+
id: { in: callRecordingIds }
|
|
783
|
+
});
|
|
784
|
+
};
|
|
785
|
+
|
|
786
|
+
// src/logic-functions/data/get-current-workspace-id.util.ts
|
|
787
|
+
var import_guards11 = __toESM(require_build());
|
|
788
|
+
|
|
789
|
+
// src/logic-functions/utils/as-record.util.ts
|
|
790
|
+
var import_guards10 = __toESM(require_build());
|
|
791
|
+
var asRecord = (value) => (0, import_guards10.isObject)(value) && !(0, import_guards10.isArray)(value) ? value : void 0;
|
|
792
|
+
|
|
793
|
+
// src/logic-functions/utils/get-string.util.ts
|
|
794
|
+
var getString = (value) => isNonEmptyString(value) ? value : void 0;
|
|
795
|
+
|
|
796
|
+
// src/logic-functions/data/get-current-workspace-id.util.ts
|
|
797
|
+
var APP_ACCESS_TOKEN_ENV_VAR_NAME = "TWENTY_APP_ACCESS_TOKEN";
|
|
798
|
+
var getCurrentWorkspaceId = () => {
|
|
799
|
+
const accessToken = getString(process.env[APP_ACCESS_TOKEN_ENV_VAR_NAME]);
|
|
800
|
+
if ((0, import_guards11.isUndefined)(accessToken)) {
|
|
801
|
+
return void 0;
|
|
802
|
+
}
|
|
803
|
+
return getWorkspaceIdFromAccessToken(accessToken);
|
|
804
|
+
};
|
|
805
|
+
var getWorkspaceIdFromAccessToken = (accessToken) => {
|
|
806
|
+
const encodedPayload = accessToken.split(".")[1];
|
|
807
|
+
if ((0, import_guards11.isUndefined)(encodedPayload)) {
|
|
808
|
+
return void 0;
|
|
809
|
+
}
|
|
810
|
+
try {
|
|
811
|
+
const payload = asRecord(
|
|
812
|
+
JSON.parse(Buffer.from(encodedPayload, "base64url").toString("utf8"))
|
|
813
|
+
);
|
|
814
|
+
return getString(payload?.workspaceId);
|
|
815
|
+
} catch {
|
|
816
|
+
return void 0;
|
|
817
|
+
}
|
|
818
|
+
};
|
|
819
|
+
|
|
820
|
+
// src/logic-functions/recall-api/schedule-recall-bot.util.ts
|
|
821
|
+
var import_guards13 = __toESM(require_build());
|
|
822
|
+
|
|
823
|
+
// src/logic-functions/constants/recall-bot-automatic-leave.ts
|
|
824
|
+
var import_guards12 = __toESM(require_build());
|
|
825
|
+
|
|
826
|
+
// src/logic-functions/constants/call-recorder-everyone-left-timeout-seconds-env-var-name.ts
|
|
827
|
+
var CALL_RECORDER_EVERYONE_LEFT_TIMEOUT_SECONDS_ENV_VAR_NAME = "CALL_RECORDER_EVERYONE_LEFT_TIMEOUT_SECONDS";
|
|
828
|
+
|
|
829
|
+
// src/logic-functions/constants/call-recorder-noone-joined-timeout-seconds-env-var-name.ts
|
|
830
|
+
var CALL_RECORDER_NOONE_JOINED_TIMEOUT_SECONDS_ENV_VAR_NAME = "CALL_RECORDER_NOONE_JOINED_TIMEOUT_SECONDS";
|
|
831
|
+
|
|
832
|
+
// src/logic-functions/constants/call-recorder-waiting-room-timeout-seconds-env-var-name.ts
|
|
833
|
+
var CALL_RECORDER_WAITING_ROOM_TIMEOUT_SECONDS_ENV_VAR_NAME = "CALL_RECORDER_WAITING_ROOM_TIMEOUT_SECONDS";
|
|
834
|
+
|
|
835
|
+
// src/logic-functions/constants/recall-bot-everyone-left-min-activate-after-seconds.ts
|
|
836
|
+
var RECALL_BOT_EVERYONE_LEFT_MIN_ACTIVATE_AFTER_SECONDS = 1;
|
|
837
|
+
|
|
838
|
+
// src/logic-functions/constants/recall-bot-automatic-leave.ts
|
|
839
|
+
var getRecallBotAutomaticLeave = () => {
|
|
840
|
+
const waitingRoomTimeoutSeconds = getOptionalPositiveIntegerVariable(
|
|
841
|
+
CALL_RECORDER_WAITING_ROOM_TIMEOUT_SECONDS_ENV_VAR_NAME
|
|
842
|
+
);
|
|
843
|
+
const nooneJoinedTimeoutSeconds = getOptionalPositiveIntegerVariable(
|
|
844
|
+
CALL_RECORDER_NOONE_JOINED_TIMEOUT_SECONDS_ENV_VAR_NAME
|
|
845
|
+
);
|
|
846
|
+
const everyoneLeftTimeoutSeconds = getOptionalPositiveIntegerVariable(
|
|
847
|
+
CALL_RECORDER_EVERYONE_LEFT_TIMEOUT_SECONDS_ENV_VAR_NAME
|
|
848
|
+
);
|
|
849
|
+
const automaticLeave = {};
|
|
850
|
+
if (!(0, import_guards12.isUndefined)(waitingRoomTimeoutSeconds)) {
|
|
851
|
+
automaticLeave.waiting_room_timeout = waitingRoomTimeoutSeconds;
|
|
852
|
+
}
|
|
853
|
+
if (!(0, import_guards12.isUndefined)(nooneJoinedTimeoutSeconds)) {
|
|
854
|
+
automaticLeave.noone_joined_timeout = nooneJoinedTimeoutSeconds;
|
|
855
|
+
}
|
|
856
|
+
if (!(0, import_guards12.isUndefined)(everyoneLeftTimeoutSeconds)) {
|
|
857
|
+
automaticLeave.everyone_left_timeout = {
|
|
858
|
+
timeout: everyoneLeftTimeoutSeconds,
|
|
859
|
+
activate_after: RECALL_BOT_EVERYONE_LEFT_MIN_ACTIVATE_AFTER_SECONDS
|
|
860
|
+
};
|
|
861
|
+
}
|
|
862
|
+
return Object.keys(automaticLeave).length === 0 ? void 0 : automaticLeave;
|
|
863
|
+
};
|
|
864
|
+
var getOptionalPositiveIntegerVariable = (variableName) => {
|
|
865
|
+
const rawValue = normalizeOptionalString3(
|
|
866
|
+
getApplicationVariableValue(variableName)
|
|
867
|
+
);
|
|
868
|
+
if ((0, import_guards12.isUndefined)(rawValue)) {
|
|
869
|
+
return void 0;
|
|
870
|
+
}
|
|
871
|
+
const timeoutSeconds = Number(rawValue);
|
|
872
|
+
if (!Number.isInteger(timeoutSeconds) || timeoutSeconds <= 0) {
|
|
873
|
+
return void 0;
|
|
874
|
+
}
|
|
875
|
+
return timeoutSeconds;
|
|
876
|
+
};
|
|
877
|
+
var normalizeOptionalString3 = (value) => isNonEmptyString(value) ? value.trim() : void 0;
|
|
878
|
+
|
|
879
|
+
// src/logic-functions/constants/call-recorder-recording-retention-hours-env-var-name.ts
|
|
880
|
+
var CALL_RECORDER_RECORDING_RETENTION_HOURS_ENV_VAR_NAME = "CALL_RECORDER_RECORDING_RETENTION_HOURS";
|
|
881
|
+
|
|
882
|
+
// src/logic-functions/constants/default-call-recorder-recording-retention-hours.ts
|
|
883
|
+
var DEFAULT_CALL_RECORDER_RECORDING_RETENTION_HOURS = 166;
|
|
884
|
+
|
|
885
|
+
// src/logic-functions/constants/recall-bot-recording-config.ts
|
|
886
|
+
var getRecallBotRecordingConfig = () => {
|
|
887
|
+
const configuredRecordingRetentionHours = getApplicationVariableValue(
|
|
888
|
+
CALL_RECORDER_RECORDING_RETENTION_HOURS_ENV_VAR_NAME
|
|
889
|
+
);
|
|
890
|
+
const recordingRetentionHours = isNonEmptyString(
|
|
891
|
+
configuredRecordingRetentionHours
|
|
892
|
+
) ? Number(configuredRecordingRetentionHours.trim()) : NaN;
|
|
893
|
+
const resolvedRecordingRetentionHours = Number.isInteger(recordingRetentionHours) && recordingRetentionHours > 0 ? recordingRetentionHours : DEFAULT_CALL_RECORDER_RECORDING_RETENTION_HOURS;
|
|
894
|
+
return {
|
|
895
|
+
video_mixed_mp4: {},
|
|
896
|
+
audio_mixed_mp3: {},
|
|
897
|
+
retention: { type: "timed", hours: resolvedRecordingRetentionHours }
|
|
898
|
+
};
|
|
899
|
+
};
|
|
900
|
+
|
|
901
|
+
// src/logic-functions/recall-api/extract-recall-bot-id.util.ts
|
|
902
|
+
var extractRecallBotId = (response) => getString(response?.id) ?? getString(response?.bot_id);
|
|
903
|
+
|
|
904
|
+
// src/logic-functions/recall-api/schedule-recall-bot.util.ts
|
|
905
|
+
var scheduleRecallBot = async ({
|
|
906
|
+
meetingUrl,
|
|
907
|
+
joinAt,
|
|
908
|
+
metadata
|
|
909
|
+
}) => {
|
|
910
|
+
const configResult = getRecallApiConfig();
|
|
911
|
+
if (!configResult.success) {
|
|
912
|
+
return { ok: false, status: null, errorMessage: configResult.error };
|
|
913
|
+
}
|
|
914
|
+
const automaticLeave = getRecallBotAutomaticLeave();
|
|
915
|
+
const result = await recallBotApiRequest({
|
|
916
|
+
config: configResult.config,
|
|
917
|
+
path: "/bot/",
|
|
918
|
+
method: "POST",
|
|
919
|
+
body: {
|
|
920
|
+
meeting_url: meetingUrl,
|
|
921
|
+
join_at: joinAt,
|
|
922
|
+
bot_name: configResult.config.botName,
|
|
923
|
+
...(0, import_guards13.isUndefined)(automaticLeave) ? {} : { automatic_leave: automaticLeave },
|
|
924
|
+
recording_config: getRecallBotRecordingConfig(),
|
|
925
|
+
metadata
|
|
926
|
+
}
|
|
927
|
+
});
|
|
928
|
+
if (!result.ok) {
|
|
929
|
+
return result;
|
|
930
|
+
}
|
|
931
|
+
const externalBotId = extractRecallBotId(result.data);
|
|
932
|
+
if ((0, import_guards13.isUndefined)(externalBotId)) {
|
|
933
|
+
return {
|
|
934
|
+
ok: false,
|
|
935
|
+
status: null,
|
|
936
|
+
errorMessage: "Recall API created a bot but the response did not include a bot id"
|
|
937
|
+
};
|
|
938
|
+
}
|
|
939
|
+
return {
|
|
940
|
+
ok: true,
|
|
941
|
+
externalBotId
|
|
942
|
+
};
|
|
943
|
+
};
|
|
944
|
+
|
|
945
|
+
// src/logic-functions/flows/ensure-call-recorder.util.ts
|
|
946
|
+
var ensureCallRecorder = async (client, { callRecording, calendarEvent }) => {
|
|
947
|
+
const meetingUrl = calendarEvent.conferenceLinkUrl;
|
|
948
|
+
const meetingStartsAt = calendarEvent.startsAt;
|
|
949
|
+
if ((0, import_guards14.isUndefined)(meetingUrl) || (0, import_guards14.isUndefined)(meetingStartsAt)) {
|
|
950
|
+
return false;
|
|
951
|
+
}
|
|
952
|
+
const joinAt = computeRecallBotJoinAt(meetingStartsAt);
|
|
953
|
+
const freshCallRecording = (await findCallRecordingsByIds(client, [callRecording.id]))[0];
|
|
954
|
+
if ((0, import_guards14.isUndefined)(freshCallRecording) || freshCallRecording.recordingRequestStatus !== "REQUESTED" /* REQUESTED */ || !(0, import_guards14.isUndefined)(freshCallRecording.externalBotId)) {
|
|
955
|
+
return false;
|
|
956
|
+
}
|
|
957
|
+
const workspaceId = getCurrentWorkspaceId();
|
|
958
|
+
if ((0, import_guards14.isUndefined)(workspaceId)) {
|
|
959
|
+
console.error(
|
|
960
|
+
`[call-recorder] cannot schedule Recall bot for callRecording ${callRecording.id}: workspace id unavailable, the shared webhook could not be routed back`
|
|
961
|
+
);
|
|
962
|
+
return false;
|
|
963
|
+
}
|
|
964
|
+
const scheduleResult = await scheduleRecallBot({
|
|
965
|
+
meetingUrl,
|
|
966
|
+
joinAt,
|
|
967
|
+
metadata: buildRecallRoutingMetadata({
|
|
968
|
+
callRecordingId: callRecording.id,
|
|
969
|
+
workspaceId
|
|
970
|
+
})
|
|
971
|
+
});
|
|
972
|
+
if (!scheduleResult.ok) {
|
|
973
|
+
console.warn(
|
|
974
|
+
`[call-recorder] failed to schedule Recall bot for callRecording ${callRecording.id}: ${scheduleResult.errorMessage}`
|
|
975
|
+
);
|
|
976
|
+
return false;
|
|
977
|
+
}
|
|
978
|
+
await updateCallRecording(client, {
|
|
979
|
+
id: callRecording.id,
|
|
980
|
+
data: { externalBotId: scheduleResult.externalBotId }
|
|
981
|
+
});
|
|
982
|
+
return true;
|
|
983
|
+
};
|
|
984
|
+
|
|
985
|
+
// src/logic-functions/data/fetch-calendar-events-by-filter.util.ts
|
|
986
|
+
var import_guards15 = __toESM(require_build());
|
|
987
|
+
|
|
988
|
+
// src/logic-functions/constants/restricted-field-placeholder.ts
|
|
989
|
+
var RESTRICTED_FIELD_PLACEHOLDER = "FIELD_RESTRICTED_ADDITIONAL_PERMISSIONS_REQUIRED";
|
|
990
|
+
|
|
991
|
+
// src/logic-functions/data/strip-restricted-field-value.util.ts
|
|
992
|
+
var stripRestrictedFieldValue = (value) => value === RESTRICTED_FIELD_PLACEHOLDER ? void 0 : value;
|
|
993
|
+
|
|
994
|
+
// src/logic-functions/data/fetch-calendar-events-by-filter.util.ts
|
|
995
|
+
var fetchCalendarEventsByFilter = async (client, filter) => {
|
|
996
|
+
const calendarEventNodes = await fetchAllNodes(
|
|
997
|
+
async (afterCursor) => {
|
|
998
|
+
const queryResult = await client.query({
|
|
999
|
+
calendarEvents: {
|
|
1000
|
+
__args: {
|
|
1001
|
+
filter,
|
|
1002
|
+
first: TWENTY_PAGE_SIZE,
|
|
1003
|
+
...(0, import_guards15.isUndefined)(afterCursor) ? {} : { after: afterCursor }
|
|
1004
|
+
},
|
|
1005
|
+
pageInfo: {
|
|
1006
|
+
hasNextPage: true,
|
|
1007
|
+
endCursor: true
|
|
1008
|
+
},
|
|
1009
|
+
edges: {
|
|
1010
|
+
node: {
|
|
1011
|
+
id: true,
|
|
1012
|
+
title: true,
|
|
1013
|
+
isCanceled: true,
|
|
1014
|
+
startsAt: true,
|
|
1015
|
+
endsAt: true,
|
|
1016
|
+
iCalUid: true,
|
|
1017
|
+
conferenceLink: {
|
|
1018
|
+
primaryLinkUrl: true
|
|
1019
|
+
},
|
|
1020
|
+
callRecorderPreference: true
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1024
|
+
});
|
|
1025
|
+
return queryResult.calendarEvents;
|
|
1026
|
+
}
|
|
1027
|
+
);
|
|
1028
|
+
return calendarEventNodes.map((calendarEvent) => ({
|
|
1029
|
+
id: calendarEvent.id,
|
|
1030
|
+
title: stripRestrictedFieldValue(calendarEvent.title ?? void 0),
|
|
1031
|
+
isCanceled: calendarEvent.isCanceled ?? false,
|
|
1032
|
+
startsAt: calendarEvent.startsAt ?? void 0,
|
|
1033
|
+
endsAt: calendarEvent.endsAt ?? void 0,
|
|
1034
|
+
iCalUid: calendarEvent.iCalUid ?? void 0,
|
|
1035
|
+
conferenceLinkUrl: isNonEmptyString(
|
|
1036
|
+
calendarEvent.conferenceLink?.primaryLinkUrl
|
|
1037
|
+
) ? calendarEvent.conferenceLink.primaryLinkUrl : void 0,
|
|
1038
|
+
callRecorderPreference: (0, import_guards15.isString)(calendarEvent.callRecorderPreference) ? calendarEvent.callRecorderPreference : void 0
|
|
1039
|
+
}));
|
|
1040
|
+
};
|
|
1041
|
+
|
|
1042
|
+
// src/logic-functions/data/fetch-calendar-events-by-ids.util.ts
|
|
1043
|
+
var fetchCalendarEventsByIds = async (client, calendarEventIds) => {
|
|
1044
|
+
const uniqueCalendarEventIds = getUniqueSortedIds(calendarEventIds);
|
|
1045
|
+
if (uniqueCalendarEventIds.length === 0) {
|
|
1046
|
+
return [];
|
|
1047
|
+
}
|
|
1048
|
+
return fetchCalendarEventsByFilter(client, {
|
|
1049
|
+
id: { in: uniqueCalendarEventIds }
|
|
1050
|
+
});
|
|
1051
|
+
};
|
|
1052
|
+
|
|
1053
|
+
// src/logic-functions/data/fetch-calendar-events-by-starts-at-values.util.ts
|
|
1054
|
+
var fetchCalendarEventsByStartsAtValues = async (client, startsAtValues) => {
|
|
1055
|
+
const uniqueStartsAtValues = [...new Set(startsAtValues)].sort();
|
|
1056
|
+
if (uniqueStartsAtValues.length === 0) {
|
|
1057
|
+
return [];
|
|
1058
|
+
}
|
|
1059
|
+
return fetchCalendarEventsByFilter(client, {
|
|
1060
|
+
startsAt: { in: uniqueStartsAtValues }
|
|
1061
|
+
});
|
|
1062
|
+
};
|
|
1063
|
+
|
|
1064
|
+
// src/logic-functions/data/find-call-recordings-by-calendar-event-ids.util.ts
|
|
1065
|
+
var findCallRecordingsByCalendarEventIds = async (client, calendarEventIds) => {
|
|
1066
|
+
if (calendarEventIds.length === 0) {
|
|
1067
|
+
return [];
|
|
1068
|
+
}
|
|
1069
|
+
return findCallRecordingsByFilter(client, {
|
|
1070
|
+
calendarEventId: { in: calendarEventIds }
|
|
1071
|
+
});
|
|
1072
|
+
};
|
|
1073
|
+
|
|
1074
|
+
// src/logic-functions/flows/reschedule-call-recording-bot.util.ts
|
|
1075
|
+
var import_guards17 = __toESM(require_build());
|
|
1076
|
+
|
|
1077
|
+
// src/logic-functions/recall-api/reschedule-recall-bot.util.ts
|
|
1078
|
+
var import_guards16 = __toESM(require_build());
|
|
1079
|
+
var rescheduleRecallBot = async ({
|
|
1080
|
+
externalBotId,
|
|
1081
|
+
meetingUrl,
|
|
1082
|
+
joinAt,
|
|
1083
|
+
metadata
|
|
1084
|
+
}) => {
|
|
1085
|
+
const configResult = getRecallApiConfig();
|
|
1086
|
+
if (!configResult.success) {
|
|
1087
|
+
return { ok: false, status: null, errorMessage: configResult.error };
|
|
1088
|
+
}
|
|
1089
|
+
const automaticLeave = getRecallBotAutomaticLeave();
|
|
1090
|
+
const result = await recallBotApiRequest({
|
|
1091
|
+
config: configResult.config,
|
|
1092
|
+
path: `/bot/${externalBotId}/`,
|
|
1093
|
+
method: "PATCH",
|
|
1094
|
+
body: {
|
|
1095
|
+
meeting_url: meetingUrl,
|
|
1096
|
+
join_at: joinAt,
|
|
1097
|
+
bot_name: configResult.config.botName,
|
|
1098
|
+
...(0, import_guards16.isUndefined)(automaticLeave) ? {} : { automatic_leave: automaticLeave },
|
|
1099
|
+
recording_config: getRecallBotRecordingConfig(),
|
|
1100
|
+
metadata
|
|
1101
|
+
}
|
|
1102
|
+
});
|
|
1103
|
+
if (!result.ok) {
|
|
1104
|
+
return result;
|
|
1105
|
+
}
|
|
1106
|
+
return {
|
|
1107
|
+
ok: true,
|
|
1108
|
+
externalBotId: extractRecallBotId(result.data) ?? externalBotId
|
|
1109
|
+
};
|
|
1110
|
+
};
|
|
1111
|
+
|
|
1112
|
+
// src/logic-functions/flows/reschedule-call-recording-bot.util.ts
|
|
1113
|
+
var RECALL_BOT_NOT_FOUND_STATUS = 404;
|
|
1114
|
+
var rescheduleCallRecordingBot = async (client, { callRecording, calendarEvent }) => {
|
|
1115
|
+
const externalBotId = callRecording.externalBotId;
|
|
1116
|
+
if ((0, import_guards17.isUndefined)(externalBotId)) {
|
|
1117
|
+
return;
|
|
1118
|
+
}
|
|
1119
|
+
const meetingUrl = calendarEvent.conferenceLinkUrl;
|
|
1120
|
+
const meetingStartsAt = calendarEvent.startsAt;
|
|
1121
|
+
if ((0, import_guards17.isUndefined)(meetingUrl) || (0, import_guards17.isUndefined)(meetingStartsAt)) {
|
|
1122
|
+
return;
|
|
1123
|
+
}
|
|
1124
|
+
const joinAt = computeRecallBotJoinAt(meetingStartsAt);
|
|
1125
|
+
const workspaceId = getCurrentWorkspaceId();
|
|
1126
|
+
if ((0, import_guards17.isUndefined)(workspaceId)) {
|
|
1127
|
+
console.warn(
|
|
1128
|
+
`[call-recorder] cannot reschedule Recall bot for callRecording ${callRecording.id}: workspace id unavailable`
|
|
1129
|
+
);
|
|
1130
|
+
return;
|
|
1131
|
+
}
|
|
1132
|
+
const rescheduleResult = await rescheduleRecallBot({
|
|
1133
|
+
externalBotId,
|
|
1134
|
+
meetingUrl,
|
|
1135
|
+
joinAt,
|
|
1136
|
+
metadata: buildRecallRoutingMetadata({
|
|
1137
|
+
callRecordingId: callRecording.id,
|
|
1138
|
+
workspaceId
|
|
1139
|
+
})
|
|
1140
|
+
});
|
|
1141
|
+
if (rescheduleResult.ok) {
|
|
1142
|
+
return;
|
|
1143
|
+
}
|
|
1144
|
+
if (rescheduleResult.status === RECALL_BOT_NOT_FOUND_STATUS) {
|
|
1145
|
+
await updateCallRecording(client, {
|
|
1146
|
+
id: callRecording.id,
|
|
1147
|
+
data: { externalBotId: null }
|
|
1148
|
+
});
|
|
1149
|
+
return;
|
|
1150
|
+
}
|
|
1151
|
+
console.warn(
|
|
1152
|
+
`[call-recorder] failed to update Recall bot for callRecording ${callRecording.id}: ${rescheduleResult.errorMessage}`
|
|
1153
|
+
);
|
|
1154
|
+
};
|
|
1155
|
+
|
|
1156
|
+
// src/logic-functions/flows/reconcile-call-recorder.util.ts
|
|
1157
|
+
var reconcileCallRecorderForCalendarEventIds = async ({
|
|
1158
|
+
client,
|
|
1159
|
+
calendarEventIds,
|
|
1160
|
+
removedOccurrences = [],
|
|
1161
|
+
now = /* @__PURE__ */ new Date()
|
|
1162
|
+
}) => {
|
|
1163
|
+
const meetingPolicyResults = await resolveCallRecorderPolicyResultsForMeetings({
|
|
1164
|
+
client,
|
|
1165
|
+
calendarEventIds,
|
|
1166
|
+
removedOccurrences,
|
|
1167
|
+
now
|
|
1168
|
+
});
|
|
1169
|
+
return reconcileCallRecorderForMeetingOccurrences({
|
|
1170
|
+
client,
|
|
1171
|
+
meetingPolicyResults,
|
|
1172
|
+
removedOccurrences
|
|
1173
|
+
});
|
|
1174
|
+
};
|
|
1175
|
+
var resolveCallRecorderPolicyResultsForMeetings = async ({
|
|
1176
|
+
client,
|
|
1177
|
+
calendarEventIds,
|
|
1178
|
+
removedOccurrences = [],
|
|
1179
|
+
now = /* @__PURE__ */ new Date()
|
|
1180
|
+
}) => {
|
|
1181
|
+
const changedCalendarEvents = await fetchCalendarEventsByIds(
|
|
1182
|
+
client,
|
|
1183
|
+
getUniqueSortedIds(calendarEventIds)
|
|
1184
|
+
);
|
|
1185
|
+
const affectedMeetingKeys = /* @__PURE__ */ new Set();
|
|
1186
|
+
const occurrenceStartsAtAnchors = /* @__PURE__ */ new Set();
|
|
1187
|
+
const changedCalendarEventPolicyResults = changedCalendarEvents.map(
|
|
1188
|
+
(calendarEvent) => buildCallRecorderPolicyResult(calendarEvent, now)
|
|
1189
|
+
);
|
|
1190
|
+
for (const policyResult of changedCalendarEventPolicyResults) {
|
|
1191
|
+
affectedMeetingKeys.add(policyResult.realMeetingKey);
|
|
1192
|
+
}
|
|
1193
|
+
for (const calendarEvent of changedCalendarEvents) {
|
|
1194
|
+
if (!(0, import_guards18.isUndefined)(calendarEvent.startsAt)) {
|
|
1195
|
+
occurrenceStartsAtAnchors.add(calendarEvent.startsAt);
|
|
1196
|
+
}
|
|
1197
|
+
}
|
|
1198
|
+
for (const removedOccurrence of removedOccurrences) {
|
|
1199
|
+
affectedMeetingKeys.add(removedOccurrence.realMeetingKey);
|
|
1200
|
+
if (!(0, import_guards18.isUndefined)(removedOccurrence.startsAt)) {
|
|
1201
|
+
occurrenceStartsAtAnchors.add(removedOccurrence.startsAt);
|
|
1202
|
+
}
|
|
1203
|
+
}
|
|
1204
|
+
if (affectedMeetingKeys.size === 0) {
|
|
1205
|
+
return [];
|
|
1206
|
+
}
|
|
1207
|
+
const occurrenceSiblingEvents = await fetchCalendarEventsByStartsAtValues(
|
|
1208
|
+
client,
|
|
1209
|
+
[...occurrenceStartsAtAnchors]
|
|
1210
|
+
);
|
|
1211
|
+
const policyResultsByCalendarEventId = new Map(
|
|
1212
|
+
changedCalendarEventPolicyResults.map((policyResult) => [
|
|
1213
|
+
policyResult.calendarEventId,
|
|
1214
|
+
policyResult
|
|
1215
|
+
])
|
|
1216
|
+
);
|
|
1217
|
+
for (const calendarEvent of occurrenceSiblingEvents) {
|
|
1218
|
+
if (policyResultsByCalendarEventId.has(calendarEvent.id)) {
|
|
1219
|
+
continue;
|
|
1220
|
+
}
|
|
1221
|
+
policyResultsByCalendarEventId.set(
|
|
1222
|
+
calendarEvent.id,
|
|
1223
|
+
buildCallRecorderPolicyResult(calendarEvent, now)
|
|
1224
|
+
);
|
|
1225
|
+
}
|
|
1226
|
+
const perCalendarEventPolicyResults = [
|
|
1227
|
+
...policyResultsByCalendarEventId.values()
|
|
1228
|
+
].filter(
|
|
1229
|
+
(policyResult) => affectedMeetingKeys.has(policyResult.realMeetingKey)
|
|
1230
|
+
).map((policyResult) => ({
|
|
1231
|
+
calendarEventId: policyResult.calendarEventId,
|
|
1232
|
+
realMeetingKey: policyResult.realMeetingKey,
|
|
1233
|
+
shouldRequestBot: policyResult.shouldRequestBot
|
|
1234
|
+
}));
|
|
1235
|
+
const meetingPolicyResults = aggregateCallRecorderPolicyResultsByMeeting(
|
|
1236
|
+
perCalendarEventPolicyResults
|
|
1237
|
+
);
|
|
1238
|
+
const meetingKeysWithPolicyResult = new Set(
|
|
1239
|
+
meetingPolicyResults.map(
|
|
1240
|
+
(meetingPolicyResult) => meetingPolicyResult.realMeetingKey
|
|
1241
|
+
)
|
|
1242
|
+
);
|
|
1243
|
+
for (const meetingKey of [...affectedMeetingKeys].sort()) {
|
|
1244
|
+
if (meetingKeysWithPolicyResult.has(meetingKey)) {
|
|
1245
|
+
continue;
|
|
1246
|
+
}
|
|
1247
|
+
meetingPolicyResults.push({
|
|
1248
|
+
realMeetingKey: meetingKey,
|
|
1249
|
+
shouldRequestBot: false,
|
|
1250
|
+
calendarEventIds: [],
|
|
1251
|
+
requestingCalendarEventIds: []
|
|
1252
|
+
});
|
|
1253
|
+
}
|
|
1254
|
+
return meetingPolicyResults;
|
|
1255
|
+
};
|
|
1256
|
+
var reconcileCallRecorderForMeetingOccurrences = async ({
|
|
1257
|
+
client,
|
|
1258
|
+
meetingPolicyResults,
|
|
1259
|
+
removedOccurrences = []
|
|
1260
|
+
}) => {
|
|
1261
|
+
const removedCalendarEventIdsByMeetingKey = buildRemovedCalendarEventIdsByMeetingKey(removedOccurrences);
|
|
1262
|
+
const reconciliationResults = [];
|
|
1263
|
+
const orderedMeetingPolicyResults = [
|
|
1264
|
+
...meetingPolicyResults.filter(
|
|
1265
|
+
(meetingPolicyResult) => !meetingPolicyResult.shouldRequestBot
|
|
1266
|
+
),
|
|
1267
|
+
...meetingPolicyResults.filter(
|
|
1268
|
+
(meetingPolicyResult) => meetingPolicyResult.shouldRequestBot
|
|
1269
|
+
)
|
|
1270
|
+
];
|
|
1271
|
+
for (const meetingPolicyResult of orderedMeetingPolicyResults) {
|
|
1272
|
+
const removedCalendarEventIds = removedCalendarEventIdsByMeetingKey.get(
|
|
1273
|
+
meetingPolicyResult.realMeetingKey
|
|
1274
|
+
) ?? [];
|
|
1275
|
+
try {
|
|
1276
|
+
reconciliationResults.push(
|
|
1277
|
+
meetingPolicyResult.shouldRequestBot ? await reconcileActiveMeeting({
|
|
1278
|
+
client,
|
|
1279
|
+
meetingPolicyResult,
|
|
1280
|
+
removedCalendarEventIds
|
|
1281
|
+
}) : await reconcileCanceledMeeting({
|
|
1282
|
+
client,
|
|
1283
|
+
meetingPolicyResult,
|
|
1284
|
+
removedCalendarEventIds
|
|
1285
|
+
})
|
|
1286
|
+
);
|
|
1287
|
+
} catch (error) {
|
|
1288
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1289
|
+
console.error(
|
|
1290
|
+
`[call-recorder] reconciliation failed for meeting ${meetingPolicyResult.realMeetingKey}: ${errorMessage}`
|
|
1291
|
+
);
|
|
1292
|
+
reconciliationResults.push({
|
|
1293
|
+
action: "FAILED",
|
|
1294
|
+
realMeetingKey: meetingPolicyResult.realMeetingKey,
|
|
1295
|
+
errorMessage
|
|
1296
|
+
});
|
|
1297
|
+
}
|
|
1298
|
+
}
|
|
1299
|
+
return reconciliationResults;
|
|
1300
|
+
};
|
|
1301
|
+
var reconcileActiveMeeting = async ({
|
|
1302
|
+
client,
|
|
1303
|
+
meetingPolicyResult,
|
|
1304
|
+
removedCalendarEventIds
|
|
1305
|
+
}) => {
|
|
1306
|
+
const representativeCalendarEventId = getUniqueSortedIds(
|
|
1307
|
+
meetingPolicyResult.requestingCalendarEventIds
|
|
1308
|
+
)[0];
|
|
1309
|
+
if ((0, import_guards18.isUndefined)(representativeCalendarEventId)) {
|
|
1310
|
+
return buildSkippedResult(meetingPolicyResult.realMeetingKey);
|
|
1311
|
+
}
|
|
1312
|
+
const representativeCalendarEvent = (await fetchCalendarEventsByIds(client, [representativeCalendarEventId]))[0];
|
|
1313
|
+
if ((0, import_guards18.isUndefined)(representativeCalendarEvent)) {
|
|
1314
|
+
return buildSkippedResult(meetingPolicyResult.realMeetingKey);
|
|
1315
|
+
}
|
|
1316
|
+
const callRecordingId = computeCallRecordingIdForMeeting(
|
|
1317
|
+
meetingPolicyResult.realMeetingKey
|
|
1318
|
+
);
|
|
1319
|
+
const existingCallRecording = (await findCallRecordingsByIds(client, [callRecordingId]))[0];
|
|
1320
|
+
if (!(0, import_guards18.isUndefined)(existingCallRecording)) {
|
|
1321
|
+
return updatePolicyManagedCallRecording({
|
|
1322
|
+
client,
|
|
1323
|
+
existingCallRecording,
|
|
1324
|
+
representativeCalendarEvent,
|
|
1325
|
+
realMeetingKey: meetingPolicyResult.realMeetingKey
|
|
1326
|
+
});
|
|
1327
|
+
}
|
|
1328
|
+
const manualOpenCallRecording = await findManualOpenCallRecording({
|
|
1329
|
+
client,
|
|
1330
|
+
meetingPolicyResult,
|
|
1331
|
+
removedCalendarEventIds
|
|
1332
|
+
});
|
|
1333
|
+
if (!(0, import_guards18.isUndefined)(manualOpenCallRecording)) {
|
|
1334
|
+
return {
|
|
1335
|
+
action: "SKIPPED",
|
|
1336
|
+
realMeetingKey: meetingPolicyResult.realMeetingKey,
|
|
1337
|
+
callRecordingId: manualOpenCallRecording.id
|
|
1338
|
+
};
|
|
1339
|
+
}
|
|
1340
|
+
return createPolicyManagedCallRecording({
|
|
1341
|
+
client,
|
|
1342
|
+
callRecordingId,
|
|
1343
|
+
representativeCalendarEvent,
|
|
1344
|
+
realMeetingKey: meetingPolicyResult.realMeetingKey
|
|
1345
|
+
});
|
|
1346
|
+
};
|
|
1347
|
+
var updatePolicyManagedCallRecording = async ({
|
|
1348
|
+
client,
|
|
1349
|
+
existingCallRecording,
|
|
1350
|
+
representativeCalendarEvent,
|
|
1351
|
+
realMeetingKey
|
|
1352
|
+
}) => {
|
|
1353
|
+
await updateCallRecording(client, {
|
|
1354
|
+
id: existingCallRecording.id,
|
|
1355
|
+
data: buildPolicyManagedCallRecordingUpdateFields({
|
|
1356
|
+
existingCallRecording,
|
|
1357
|
+
calendarEvent: representativeCalendarEvent
|
|
1358
|
+
})
|
|
1359
|
+
});
|
|
1360
|
+
await rescheduleCallRecordingBot(client, {
|
|
1361
|
+
callRecording: existingCallRecording,
|
|
1362
|
+
calendarEvent: representativeCalendarEvent
|
|
1363
|
+
});
|
|
1364
|
+
return {
|
|
1365
|
+
action: "UPDATED",
|
|
1366
|
+
realMeetingKey,
|
|
1367
|
+
callRecordingId: existingCallRecording.id
|
|
1368
|
+
};
|
|
1369
|
+
};
|
|
1370
|
+
var createPolicyManagedCallRecording = async ({
|
|
1371
|
+
client,
|
|
1372
|
+
callRecordingId,
|
|
1373
|
+
representativeCalendarEvent,
|
|
1374
|
+
realMeetingKey
|
|
1375
|
+
}) => {
|
|
1376
|
+
const scheduledFields = buildScheduledCallRecordingFields(
|
|
1377
|
+
representativeCalendarEvent
|
|
1378
|
+
);
|
|
1379
|
+
try {
|
|
1380
|
+
await createCallRecording(client, {
|
|
1381
|
+
id: callRecordingId,
|
|
1382
|
+
data: scheduledFields
|
|
1383
|
+
});
|
|
1384
|
+
} catch (error) {
|
|
1385
|
+
const concurrentlyCreatedCallRecording = (await findCallRecordingsByIds(client, [callRecordingId]))[0];
|
|
1386
|
+
if ((0, import_guards18.isUndefined)(concurrentlyCreatedCallRecording)) {
|
|
1387
|
+
throw error;
|
|
1388
|
+
}
|
|
1389
|
+
return updatePolicyManagedCallRecording({
|
|
1390
|
+
client,
|
|
1391
|
+
existingCallRecording: concurrentlyCreatedCallRecording,
|
|
1392
|
+
representativeCalendarEvent,
|
|
1393
|
+
realMeetingKey
|
|
1394
|
+
});
|
|
1395
|
+
}
|
|
1396
|
+
await ensureCallRecorder(client, {
|
|
1397
|
+
callRecording: {
|
|
1398
|
+
id: callRecordingId,
|
|
1399
|
+
...scheduledFields,
|
|
1400
|
+
title: scheduledFields.title ?? void 0
|
|
1401
|
+
},
|
|
1402
|
+
calendarEvent: representativeCalendarEvent
|
|
1403
|
+
});
|
|
1404
|
+
return {
|
|
1405
|
+
action: "CREATED",
|
|
1406
|
+
realMeetingKey,
|
|
1407
|
+
callRecordingId
|
|
1408
|
+
};
|
|
1409
|
+
};
|
|
1410
|
+
var findManualOpenCallRecording = async ({
|
|
1411
|
+
client,
|
|
1412
|
+
meetingPolicyResult,
|
|
1413
|
+
removedCalendarEventIds
|
|
1414
|
+
}) => {
|
|
1415
|
+
const calendarEventIds = getUniqueSortedIds([
|
|
1416
|
+
...meetingPolicyResult.calendarEventIds,
|
|
1417
|
+
...meetingPolicyResult.requestingCalendarEventIds,
|
|
1418
|
+
...removedCalendarEventIds
|
|
1419
|
+
]);
|
|
1420
|
+
const callRecordings = await findCallRecordingsByCalendarEventIds(
|
|
1421
|
+
client,
|
|
1422
|
+
calendarEventIds
|
|
1423
|
+
);
|
|
1424
|
+
return [...callRecordings].sort(
|
|
1425
|
+
(firstCallRecording, secondCallRecording) => firstCallRecording.id.localeCompare(secondCallRecording.id)
|
|
1426
|
+
).find(
|
|
1427
|
+
(callRecording) => callRecording.status !== "COMPLETED" /* COMPLETED */ && (0, import_guards18.isUndefined)(callRecording.recordingRequestStatus)
|
|
1428
|
+
);
|
|
1429
|
+
};
|
|
1430
|
+
var reconcileCanceledMeeting = async ({
|
|
1431
|
+
client,
|
|
1432
|
+
meetingPolicyResult,
|
|
1433
|
+
removedCalendarEventIds
|
|
1434
|
+
}) => {
|
|
1435
|
+
const calendarEventIds = getUniqueSortedIds([
|
|
1436
|
+
...meetingPolicyResult.calendarEventIds,
|
|
1437
|
+
...removedCalendarEventIds
|
|
1438
|
+
]);
|
|
1439
|
+
const cancellableCallRecordings = (await findCallRecordingsByCalendarEventIds(client, calendarEventIds)).filter(
|
|
1440
|
+
(callRecording) => callRecording.status === "SCHEDULED" /* SCHEDULED */ && callRecording.recordingRequestStatus === "REQUESTED" /* REQUESTED */
|
|
1441
|
+
);
|
|
1442
|
+
if (cancellableCallRecordings.length === 0) {
|
|
1443
|
+
return buildSkippedResult(meetingPolicyResult.realMeetingKey);
|
|
1444
|
+
}
|
|
1445
|
+
for (const callRecording of cancellableCallRecordings) {
|
|
1446
|
+
await cancelCallRecordingRequest({
|
|
1447
|
+
client,
|
|
1448
|
+
callRecording
|
|
1449
|
+
});
|
|
1450
|
+
}
|
|
1451
|
+
return {
|
|
1452
|
+
action: "CANCELED",
|
|
1453
|
+
realMeetingKey: meetingPolicyResult.realMeetingKey,
|
|
1454
|
+
callRecordingId: cancellableCallRecordings[0].id
|
|
1455
|
+
};
|
|
1456
|
+
};
|
|
1457
|
+
var buildCalendarDrivenCallRecordingFields = (calendarEvent) => ({
|
|
1458
|
+
// Wire null clears a stale title when the calendar title is gone or restricted.
|
|
1459
|
+
title: calendarEvent.title ?? null,
|
|
1460
|
+
recordingRequestStatus: "REQUESTED" /* REQUESTED */,
|
|
1461
|
+
calendarEventId: calendarEvent.id
|
|
1462
|
+
});
|
|
1463
|
+
var buildScheduledCallRecordingFields = (calendarEvent) => ({
|
|
1464
|
+
...buildCalendarDrivenCallRecordingFields(calendarEvent),
|
|
1465
|
+
status: "SCHEDULED" /* SCHEDULED */
|
|
1466
|
+
});
|
|
1467
|
+
var buildPolicyManagedCallRecordingUpdateFields = ({
|
|
1468
|
+
existingCallRecording,
|
|
1469
|
+
calendarEvent
|
|
1470
|
+
}) => canResetCallRecordingStatusToScheduled(existingCallRecording.status) ? {
|
|
1471
|
+
...buildScheduledCallRecordingFields(calendarEvent),
|
|
1472
|
+
...(0, import_guards18.isUndefined)(existingCallRecording.callRecorderFailureReason) ? {} : { callRecorderFailureReason: null }
|
|
1473
|
+
} : buildCalendarDrivenCallRecordingFields(calendarEvent);
|
|
1474
|
+
var canResetCallRecordingStatusToScheduled = (status) => status === "SCHEDULED" /* SCHEDULED */ || status === "FAILED" /* FAILED */;
|
|
1475
|
+
var buildRemovedCalendarEventIdsByMeetingKey = (removedOccurrences) => {
|
|
1476
|
+
const calendarEventIdsByMeetingKey = /* @__PURE__ */ new Map();
|
|
1477
|
+
for (const removedOccurrence of removedOccurrences) {
|
|
1478
|
+
calendarEventIdsByMeetingKey.set(removedOccurrence.realMeetingKey, [
|
|
1479
|
+
...calendarEventIdsByMeetingKey.get(removedOccurrence.realMeetingKey) ?? [],
|
|
1480
|
+
removedOccurrence.calendarEventId
|
|
1481
|
+
]);
|
|
1482
|
+
}
|
|
1483
|
+
return calendarEventIdsByMeetingKey;
|
|
1484
|
+
};
|
|
1485
|
+
var buildSkippedResult = (realMeetingKey) => ({
|
|
1486
|
+
action: "SKIPPED",
|
|
1487
|
+
realMeetingKey,
|
|
1488
|
+
callRecordingId: null
|
|
1489
|
+
});
|
|
1490
|
+
|
|
1491
|
+
// src/logic-functions/reconcile-call-recorder-calendar-event.ts
|
|
1492
|
+
var CALENDAR_EVENT_OBJECT_NAME = "calendarEvent";
|
|
1493
|
+
var CALL_RECORDER_RELEVANT_CALENDAR_EVENT_FIELDS = [
|
|
1494
|
+
"title",
|
|
1495
|
+
"callRecorderPreference",
|
|
1496
|
+
"conferenceLink",
|
|
1497
|
+
"startsAt",
|
|
1498
|
+
"endsAt",
|
|
1499
|
+
"isCanceled",
|
|
1500
|
+
"iCalUid"
|
|
1501
|
+
];
|
|
1502
|
+
var CALL_RECORDER_KEY_CALENDAR_EVENT_FIELDS = [
|
|
1503
|
+
"conferenceLink",
|
|
1504
|
+
"startsAt",
|
|
1505
|
+
"iCalUid"
|
|
1506
|
+
];
|
|
1507
|
+
var handler = async (event) => {
|
|
1508
|
+
const [objectName, action] = event.name.split(".");
|
|
1509
|
+
if (objectName !== CALENDAR_EVENT_OBJECT_NAME) {
|
|
1510
|
+
return { skipped: true, reason: "not a calendar event" };
|
|
1511
|
+
}
|
|
1512
|
+
const reconciliationPayload = buildCalendarEventReconciliationPayload({
|
|
1513
|
+
event,
|
|
1514
|
+
action
|
|
1515
|
+
});
|
|
1516
|
+
if (reconciliationPayload.calendarEventIds.length === 0 && reconciliationPayload.removedOccurrences.length === 0) {
|
|
1517
|
+
return { skipped: true, reason: "no relevant calendar event change" };
|
|
1518
|
+
}
|
|
1519
|
+
const client = new CoreApiClient();
|
|
1520
|
+
const reconciliationResults = await reconcileCallRecorderForCalendarEventIds({
|
|
1521
|
+
client,
|
|
1522
|
+
calendarEventIds: reconciliationPayload.calendarEventIds,
|
|
1523
|
+
removedOccurrences: reconciliationPayload.removedOccurrences
|
|
1524
|
+
});
|
|
1525
|
+
return {
|
|
1526
|
+
reconciled: true,
|
|
1527
|
+
calendarEventIds: reconciliationPayload.calendarEventIds,
|
|
1528
|
+
removedOccurrenceCount: reconciliationPayload.removedOccurrences.length,
|
|
1529
|
+
reconciliationResults
|
|
1530
|
+
};
|
|
1531
|
+
};
|
|
1532
|
+
var buildCalendarEventReconciliationPayload = ({
|
|
1533
|
+
event,
|
|
1534
|
+
action
|
|
1535
|
+
}) => {
|
|
1536
|
+
if (action === "created") {
|
|
1537
|
+
return {
|
|
1538
|
+
calendarEventIds: getUniqueSortedIds([
|
|
1539
|
+
event.recordId,
|
|
1540
|
+
event.properties.after?.id
|
|
1541
|
+
]),
|
|
1542
|
+
removedOccurrences: []
|
|
1543
|
+
};
|
|
1544
|
+
}
|
|
1545
|
+
if (action === "updated") {
|
|
1546
|
+
const updatedFields = event.properties.updatedFields ?? [];
|
|
1547
|
+
if (!hasRelevantFieldChange(updatedFields)) {
|
|
1548
|
+
return { calendarEventIds: [], removedOccurrences: [] };
|
|
1549
|
+
}
|
|
1550
|
+
const removedOccurrence = hasKeyFieldChange(updatedFields) ? buildRemovedOccurrence(event.properties.before) : void 0;
|
|
1551
|
+
return {
|
|
1552
|
+
calendarEventIds: getUniqueSortedIds([
|
|
1553
|
+
event.recordId,
|
|
1554
|
+
event.properties.after?.id
|
|
1555
|
+
]),
|
|
1556
|
+
removedOccurrences: (0, import_guards19.isUndefined)(removedOccurrence) ? [] : [removedOccurrence]
|
|
1557
|
+
};
|
|
1558
|
+
}
|
|
1559
|
+
if (action === "deleted" || action === "destroyed") {
|
|
1560
|
+
const removedOccurrence = buildRemovedOccurrence(event.properties.before);
|
|
1561
|
+
return {
|
|
1562
|
+
calendarEventIds: [],
|
|
1563
|
+
removedOccurrences: (0, import_guards19.isUndefined)(removedOccurrence) ? [] : [removedOccurrence]
|
|
1564
|
+
};
|
|
1565
|
+
}
|
|
1566
|
+
return { calendarEventIds: [], removedOccurrences: [] };
|
|
1567
|
+
};
|
|
1568
|
+
var hasRelevantFieldChange = (updatedFields) => updatedFields.some(
|
|
1569
|
+
(updatedField) => CALL_RECORDER_RELEVANT_CALENDAR_EVENT_FIELDS.includes(updatedField)
|
|
1570
|
+
);
|
|
1571
|
+
var hasKeyFieldChange = (updatedFields) => updatedFields.some(
|
|
1572
|
+
(updatedField) => CALL_RECORDER_KEY_CALENDAR_EVENT_FIELDS.includes(updatedField)
|
|
1573
|
+
);
|
|
1574
|
+
var buildRemovedOccurrence = (calendarEvent) => {
|
|
1575
|
+
if ((0, import_guards19.isUndefined)(calendarEvent)) {
|
|
1576
|
+
return void 0;
|
|
1577
|
+
}
|
|
1578
|
+
return {
|
|
1579
|
+
calendarEventId: calendarEvent.id,
|
|
1580
|
+
realMeetingKey: computeRealMeetingKey({
|
|
1581
|
+
calendarEventId: calendarEvent.id,
|
|
1582
|
+
conferenceLinkUrl: calendarEvent.conferenceLink?.primaryLinkUrl,
|
|
1583
|
+
iCalUid: calendarEvent.iCalUid ?? void 0,
|
|
1584
|
+
startsAt: calendarEvent.startsAt ?? void 0
|
|
1585
|
+
}),
|
|
1586
|
+
startsAt: calendarEvent.startsAt ?? void 0
|
|
1587
|
+
};
|
|
1588
|
+
};
|
|
1589
|
+
var reconcile_call_recorder_calendar_event_default = defineLogicFunction({
|
|
1590
|
+
universalIdentifier: CALENDAR_EVENT_RECONCILIATION_LOGIC_FUNCTION_UNIVERSAL_IDENTIFIER,
|
|
1591
|
+
name: "reconcile-call-recorder-calendar-event",
|
|
1592
|
+
description: "Reconciles app-managed Recall bot recording requests when calendar events change.",
|
|
1593
|
+
timeoutSeconds: 60,
|
|
1594
|
+
handler,
|
|
1595
|
+
databaseEventTriggerSettings: {
|
|
1596
|
+
eventName: `${CALENDAR_EVENT_OBJECT_NAME}.*`
|
|
1597
|
+
}
|
|
1598
|
+
});
|
|
1599
|
+
export {
|
|
1600
|
+
reconcile_call_recorder_calendar_event_default as default
|
|
1601
|
+
};
|
|
1602
|
+
//# sourceMappingURL=reconcile-call-recorder-calendar-event.mjs.map
|