@absolutejs/voice 0.0.22-beta.113 → 0.0.22-beta.115
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/dist/campaign.d.ts +30 -0
- package/dist/campaignDialers.d.ts +52 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +372 -20
- package/dist/testing/index.js +4 -1
- package/package.json +1 -1
package/dist/campaign.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Elysia } from 'elysia';
|
|
2
2
|
import type { VoiceRedisTaskLeaseCoordinator } from './queue';
|
|
3
|
+
import type { VoiceTelephonyOutcomeDecision, VoiceTelephonyOutcomeProviderEvent, VoiceTelephonyWebhookDecision } from './telephonyOutcome';
|
|
3
4
|
export type VoiceCampaignStatus = 'canceled' | 'completed' | 'draft' | 'paused' | 'running';
|
|
4
5
|
export type VoiceCampaignRecipientStatus = 'canceled' | 'completed' | 'failed' | 'pending' | 'queued';
|
|
5
6
|
export type VoiceCampaignAttemptStatus = 'canceled' | 'failed' | 'queued' | 'running' | 'succeeded';
|
|
@@ -251,12 +252,41 @@ export type VoiceCampaignObservabilityReport = {
|
|
|
251
252
|
};
|
|
252
253
|
summary: VoiceCampaignSummary;
|
|
253
254
|
};
|
|
255
|
+
export type VoiceCampaignTelephonyOutcomeInput<TResult = unknown> = {
|
|
256
|
+
campaignId?: string;
|
|
257
|
+
decision: VoiceTelephonyOutcomeDecision;
|
|
258
|
+
event?: VoiceTelephonyOutcomeProviderEvent;
|
|
259
|
+
externalCallId?: string;
|
|
260
|
+
routeResult?: TResult;
|
|
261
|
+
sessionId?: string;
|
|
262
|
+
attemptId?: string;
|
|
263
|
+
};
|
|
264
|
+
export type VoiceCampaignTelephonyOutcomeStatus = 'failed' | 'ignore' | 'succeeded';
|
|
265
|
+
export type VoiceCampaignTelephonyOutcomeOptions<TResult = unknown> = {
|
|
266
|
+
resolveCampaignId?: (input: VoiceCampaignTelephonyOutcomeInput<TResult>) => Promise<string | undefined> | string | undefined;
|
|
267
|
+
resolveExternalCallId?: (input: VoiceCampaignTelephonyOutcomeInput<TResult>) => Promise<string | undefined> | string | undefined;
|
|
268
|
+
resolveAttemptId?: (input: VoiceCampaignTelephonyOutcomeInput<TResult>) => Promise<string | undefined> | string | undefined;
|
|
269
|
+
runtime?: VoiceCampaignRuntime;
|
|
270
|
+
statusForDecision?: (input: VoiceCampaignTelephonyOutcomeInput<TResult>) => Promise<VoiceCampaignTelephonyOutcomeStatus> | VoiceCampaignTelephonyOutcomeStatus;
|
|
271
|
+
store?: VoiceCampaignStore;
|
|
272
|
+
};
|
|
273
|
+
export type VoiceCampaignTelephonyOutcomeResult = {
|
|
274
|
+
applied: boolean;
|
|
275
|
+
campaignId?: string;
|
|
276
|
+
error?: string;
|
|
277
|
+
externalCallId?: string;
|
|
278
|
+
reason?: 'ignored' | 'missing-attempt' | 'missing-campaign' | 'missing-runtime' | 'terminal-attempt';
|
|
279
|
+
status?: 'failed' | 'succeeded';
|
|
280
|
+
attemptId?: string;
|
|
281
|
+
};
|
|
254
282
|
export declare const createVoiceMemoryCampaignStore: () => VoiceCampaignStore;
|
|
255
283
|
export declare const summarizeVoiceCampaigns: (records: VoiceCampaignRecord[]) => VoiceCampaignSummary;
|
|
256
284
|
export declare const buildVoiceCampaignObservabilityReport: (records: VoiceCampaignRecord[], options?: VoiceCampaignObservabilityOptions) => Promise<VoiceCampaignObservabilityReport>;
|
|
257
285
|
export declare const createVoiceCampaign: (options: VoiceCampaignRuntimeOptions) => VoiceCampaignRuntime;
|
|
258
286
|
export declare const createVoiceCampaignWorker: (options: VoiceCampaignWorkerOptions) => VoiceCampaignWorker;
|
|
259
287
|
export declare const createVoiceCampaignWorkerLoop: (options: VoiceCampaignWorkerLoopOptions) => VoiceCampaignWorkerLoop;
|
|
288
|
+
export declare const applyVoiceCampaignTelephonyOutcome: <TResult = unknown>(input: VoiceCampaignTelephonyOutcomeInput<TResult>, options?: VoiceCampaignTelephonyOutcomeOptions<TResult>) => Promise<VoiceCampaignTelephonyOutcomeResult>;
|
|
289
|
+
export declare const createVoiceCampaignTelephonyOutcomeHandler: <TResult = unknown>(options: VoiceCampaignTelephonyOutcomeOptions<TResult>) => (input: VoiceTelephonyWebhookDecision<TResult>) => Promise<VoiceCampaignTelephonyOutcomeResult>;
|
|
260
290
|
export declare const runVoiceCampaignProof: (options?: VoiceCampaignProofOptions) => Promise<VoiceCampaignProofReport>;
|
|
261
291
|
export declare const renderVoiceCampaignsHTML: (records: VoiceCampaignRecord[], options?: {
|
|
262
292
|
title?: string;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { VoiceCampaignDialer, VoiceCampaignDialerInput } from './campaign';
|
|
2
|
+
type VoiceCampaignDialerFetch = (input: string | URL | Request, init?: RequestInit) => Promise<Response>;
|
|
3
|
+
type VoiceCampaignDialerMetadataInput = VoiceCampaignDialerInput & {
|
|
4
|
+
provider: 'plivo' | 'telnyx' | 'twilio';
|
|
5
|
+
};
|
|
6
|
+
type VoiceCampaignDialerMetadata = Record<string, string | number | boolean | undefined> | ((input: VoiceCampaignDialerMetadataInput) => Record<string, string | number | boolean | undefined> | Promise<Record<string, string | number | boolean | undefined>>);
|
|
7
|
+
type VoiceCampaignURLInput = {
|
|
8
|
+
attempt: VoiceCampaignDialerInput['attempt'];
|
|
9
|
+
campaign: VoiceCampaignDialerInput['campaign'];
|
|
10
|
+
recipient: VoiceCampaignDialerInput['recipient'];
|
|
11
|
+
};
|
|
12
|
+
type VoiceCampaignURLResolver = string | ((input: VoiceCampaignURLInput) => string | Promise<string>);
|
|
13
|
+
export type VoiceTwilioCampaignDialerOptions = {
|
|
14
|
+
accountSid: string;
|
|
15
|
+
answerMethod?: 'GET' | 'POST';
|
|
16
|
+
answerUrl: VoiceCampaignURLResolver;
|
|
17
|
+
apiBaseUrl?: string;
|
|
18
|
+
authToken: string;
|
|
19
|
+
fetch?: VoiceCampaignDialerFetch;
|
|
20
|
+
from: string;
|
|
21
|
+
machineDetection?: string;
|
|
22
|
+
metadata?: VoiceCampaignDialerMetadata;
|
|
23
|
+
statusCallbackEvents?: string[];
|
|
24
|
+
statusCallbackMethod?: 'GET' | 'POST';
|
|
25
|
+
statusCallbackUrl?: VoiceCampaignURLResolver;
|
|
26
|
+
};
|
|
27
|
+
export type VoiceTelnyxCampaignDialerOptions = {
|
|
28
|
+
apiBaseUrl?: string;
|
|
29
|
+
apiKey: string;
|
|
30
|
+
clientState?: VoiceCampaignDialerMetadata;
|
|
31
|
+
connectionId: string;
|
|
32
|
+
fetch?: VoiceCampaignDialerFetch;
|
|
33
|
+
from: string;
|
|
34
|
+
webhookUrl?: VoiceCampaignURLResolver;
|
|
35
|
+
webhookUrlMethod?: 'GET' | 'POST';
|
|
36
|
+
};
|
|
37
|
+
export type VoicePlivoCampaignDialerOptions = {
|
|
38
|
+
answerMethod?: 'GET' | 'POST';
|
|
39
|
+
answerUrl: VoiceCampaignURLResolver;
|
|
40
|
+
apiBaseUrl?: string;
|
|
41
|
+
authId: string;
|
|
42
|
+
authToken: string;
|
|
43
|
+
callbackMethod?: 'GET' | 'POST';
|
|
44
|
+
callbackUrl?: VoiceCampaignURLResolver;
|
|
45
|
+
fetch?: VoiceCampaignDialerFetch;
|
|
46
|
+
from: string;
|
|
47
|
+
metadata?: VoiceCampaignDialerMetadata;
|
|
48
|
+
};
|
|
49
|
+
export declare const createVoiceTwilioCampaignDialer: (options: VoiceTwilioCampaignDialerOptions) => VoiceCampaignDialer;
|
|
50
|
+
export declare const createVoiceTelnyxCampaignDialer: (options: VoiceTelnyxCampaignDialerOptions) => VoiceCampaignDialer;
|
|
51
|
+
export declare const createVoicePlivoCampaignDialer: (options: VoicePlivoCampaignDialerOptions) => VoiceCampaignDialer;
|
|
52
|
+
export {};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export { voice } from './plugin';
|
|
2
2
|
export { createVoiceAppKit, createVoiceAppKitRoutes, summarizeVoiceAppKitStatus } from './appKit';
|
|
3
|
-
export { buildVoiceCampaignObservabilityReport, createVoiceCampaign, createVoiceCampaignRoutes, createVoiceCampaignWorker, createVoiceCampaignWorkerLoop, createVoiceMemoryCampaignStore, renderVoiceCampaignObservabilityHTML, renderVoiceCampaignsHTML, runVoiceCampaignProof, summarizeVoiceCampaigns } from './campaign';
|
|
3
|
+
export { applyVoiceCampaignTelephonyOutcome, buildVoiceCampaignObservabilityReport, createVoiceCampaignTelephonyOutcomeHandler, createVoiceCampaign, createVoiceCampaignRoutes, createVoiceCampaignWorker, createVoiceCampaignWorkerLoop, createVoiceMemoryCampaignStore, renderVoiceCampaignObservabilityHTML, renderVoiceCampaignsHTML, runVoiceCampaignProof, summarizeVoiceCampaigns } from './campaign';
|
|
4
|
+
export { createVoicePlivoCampaignDialer, createVoiceTelnyxCampaignDialer, createVoiceTwilioCampaignDialer } from './campaignDialers';
|
|
4
5
|
export { createVoiceAssistant, createVoiceExperiment, summarizeVoiceAssistantRuns } from './assistant';
|
|
5
6
|
export { createVoiceAssistantHealthHTMLHandler, createVoiceAssistantHealthJSONHandler, createVoiceAssistantHealthRoutes, renderVoiceAssistantHealthHTML, summarizeVoiceAssistantHealth } from './assistantHealth';
|
|
6
7
|
export { createVoiceBargeInRoutes, renderVoiceBargeInHTML, summarizeVoiceBargeIn } from './bargeInRoutes';
|
package/dist/index.js
CHANGED
|
@@ -10984,6 +10984,155 @@ var createVoiceCampaignWorkerLoop = (options) => {
|
|
|
10984
10984
|
tick
|
|
10985
10985
|
};
|
|
10986
10986
|
};
|
|
10987
|
+
var firstOutcomeString = (values) => {
|
|
10988
|
+
for (const value of values) {
|
|
10989
|
+
if (typeof value === "string" && value.trim()) {
|
|
10990
|
+
return value.trim();
|
|
10991
|
+
}
|
|
10992
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
10993
|
+
return String(value);
|
|
10994
|
+
}
|
|
10995
|
+
}
|
|
10996
|
+
};
|
|
10997
|
+
var resolveDefaultCampaignOutcomeIds = (input) => {
|
|
10998
|
+
const metadata = input.event?.metadata ?? {};
|
|
10999
|
+
const decisionMetadata = input.decision.metadata ?? {};
|
|
11000
|
+
const routeResult = typeof input.routeResult === "object" && input.routeResult !== null ? input.routeResult : {};
|
|
11001
|
+
return {
|
|
11002
|
+
campaignId: input.campaignId ?? firstOutcomeString([
|
|
11003
|
+
metadata.campaignId,
|
|
11004
|
+
metadata.voiceCampaignId,
|
|
11005
|
+
decisionMetadata.campaignId,
|
|
11006
|
+
decisionMetadata.voiceCampaignId,
|
|
11007
|
+
routeResult.campaignId,
|
|
11008
|
+
routeResult.voiceCampaignId
|
|
11009
|
+
]),
|
|
11010
|
+
externalCallId: input.externalCallId ?? firstOutcomeString([
|
|
11011
|
+
metadata.externalCallId,
|
|
11012
|
+
metadata.callId,
|
|
11013
|
+
metadata.callSid,
|
|
11014
|
+
metadata.callUuid,
|
|
11015
|
+
decisionMetadata.externalCallId,
|
|
11016
|
+
decisionMetadata.callId,
|
|
11017
|
+
decisionMetadata.callSid,
|
|
11018
|
+
decisionMetadata.callUuid,
|
|
11019
|
+
routeResult.externalCallId,
|
|
11020
|
+
routeResult.callId,
|
|
11021
|
+
routeResult.callSid,
|
|
11022
|
+
routeResult.callUuid,
|
|
11023
|
+
input.sessionId
|
|
11024
|
+
]),
|
|
11025
|
+
attemptId: input.attemptId ?? firstOutcomeString([
|
|
11026
|
+
metadata.attemptId,
|
|
11027
|
+
metadata.voiceCampaignAttemptId,
|
|
11028
|
+
decisionMetadata.attemptId,
|
|
11029
|
+
decisionMetadata.voiceCampaignAttemptId,
|
|
11030
|
+
routeResult.attemptId,
|
|
11031
|
+
routeResult.voiceCampaignAttemptId
|
|
11032
|
+
])
|
|
11033
|
+
};
|
|
11034
|
+
};
|
|
11035
|
+
var defaultCampaignOutcomeStatus = (input) => {
|
|
11036
|
+
switch (input.decision.action) {
|
|
11037
|
+
case "complete":
|
|
11038
|
+
case "transfer":
|
|
11039
|
+
return "succeeded";
|
|
11040
|
+
case "escalate":
|
|
11041
|
+
case "no-answer":
|
|
11042
|
+
case "voicemail":
|
|
11043
|
+
return "failed";
|
|
11044
|
+
default:
|
|
11045
|
+
return "ignore";
|
|
11046
|
+
}
|
|
11047
|
+
};
|
|
11048
|
+
var findCampaignAttempt = async (input) => {
|
|
11049
|
+
const records = input.campaignId ? [await input.runtime.get(input.campaignId)].filter(Boolean) : await input.runtime.list();
|
|
11050
|
+
for (const record of records) {
|
|
11051
|
+
const attempt = record.attempts.find((item) => input.attemptId && item.id === input.attemptId || input.externalCallId && item.externalCallId === input.externalCallId);
|
|
11052
|
+
if (attempt) {
|
|
11053
|
+
return {
|
|
11054
|
+
attempt,
|
|
11055
|
+
record
|
|
11056
|
+
};
|
|
11057
|
+
}
|
|
11058
|
+
}
|
|
11059
|
+
};
|
|
11060
|
+
var applyVoiceCampaignTelephonyOutcome = async (input, options = {}) => {
|
|
11061
|
+
const runtime = options.runtime ?? (options.store ? createVoiceCampaign({
|
|
11062
|
+
store: options.store
|
|
11063
|
+
}) : undefined);
|
|
11064
|
+
if (!runtime) {
|
|
11065
|
+
return {
|
|
11066
|
+
applied: false,
|
|
11067
|
+
reason: "missing-runtime"
|
|
11068
|
+
};
|
|
11069
|
+
}
|
|
11070
|
+
const defaults = resolveDefaultCampaignOutcomeIds(input);
|
|
11071
|
+
const campaignId = await options.resolveCampaignId?.(input) ?? defaults.campaignId;
|
|
11072
|
+
const attemptId = await options.resolveAttemptId?.(input) ?? defaults.attemptId;
|
|
11073
|
+
const externalCallId = await options.resolveExternalCallId?.(input) ?? defaults.externalCallId;
|
|
11074
|
+
const status = await options.statusForDecision?.(input) ?? defaultCampaignOutcomeStatus(input);
|
|
11075
|
+
if (status === "ignore") {
|
|
11076
|
+
return {
|
|
11077
|
+
applied: false,
|
|
11078
|
+
campaignId,
|
|
11079
|
+
externalCallId,
|
|
11080
|
+
reason: "ignored",
|
|
11081
|
+
attemptId
|
|
11082
|
+
};
|
|
11083
|
+
}
|
|
11084
|
+
const match = await findCampaignAttempt({
|
|
11085
|
+
attemptId,
|
|
11086
|
+
campaignId,
|
|
11087
|
+
externalCallId,
|
|
11088
|
+
runtime
|
|
11089
|
+
});
|
|
11090
|
+
if (!match) {
|
|
11091
|
+
return {
|
|
11092
|
+
applied: false,
|
|
11093
|
+
campaignId,
|
|
11094
|
+
externalCallId,
|
|
11095
|
+
reason: campaignId ? "missing-attempt" : "missing-campaign",
|
|
11096
|
+
attemptId
|
|
11097
|
+
};
|
|
11098
|
+
}
|
|
11099
|
+
if (match.attempt.status === "failed" || match.attempt.status === "succeeded") {
|
|
11100
|
+
return {
|
|
11101
|
+
applied: false,
|
|
11102
|
+
campaignId: match.record.campaign.id,
|
|
11103
|
+
externalCallId: match.attempt.externalCallId ?? externalCallId,
|
|
11104
|
+
reason: "terminal-attempt",
|
|
11105
|
+
status: match.attempt.status,
|
|
11106
|
+
attemptId: match.attempt.id
|
|
11107
|
+
};
|
|
11108
|
+
}
|
|
11109
|
+
await runtime.completeAttempt(match.record.campaign.id, match.attempt.id, {
|
|
11110
|
+
error: status === "failed" ? input.decision.reason ?? input.decision.disposition ?? input.event?.reason ?? input.event?.status ?? input.decision.action : undefined,
|
|
11111
|
+
externalCallId: externalCallId ?? match.attempt.externalCallId,
|
|
11112
|
+
metadata: {
|
|
11113
|
+
telephonyAction: input.decision.action,
|
|
11114
|
+
telephonyConfidence: input.decision.confidence,
|
|
11115
|
+
telephonyDisposition: input.decision.disposition,
|
|
11116
|
+
telephonyProvider: input.event?.provider,
|
|
11117
|
+
telephonySource: input.decision.source,
|
|
11118
|
+
telephonyStatus: input.event?.status
|
|
11119
|
+
},
|
|
11120
|
+
status
|
|
11121
|
+
});
|
|
11122
|
+
return {
|
|
11123
|
+
applied: true,
|
|
11124
|
+
campaignId: match.record.campaign.id,
|
|
11125
|
+
externalCallId: externalCallId ?? match.attempt.externalCallId,
|
|
11126
|
+
status,
|
|
11127
|
+
attemptId: match.attempt.id
|
|
11128
|
+
};
|
|
11129
|
+
};
|
|
11130
|
+
var createVoiceCampaignTelephonyOutcomeHandler = (options) => (input) => applyVoiceCampaignTelephonyOutcome({
|
|
11131
|
+
decision: input.decision,
|
|
11132
|
+
event: input.event,
|
|
11133
|
+
routeResult: input.routeResult,
|
|
11134
|
+
sessionId: input.sessionId
|
|
11135
|
+
}, options);
|
|
10987
11136
|
var defaultProofRecipients = () => [
|
|
10988
11137
|
{
|
|
10989
11138
|
id: "campaign-proof-recipient-1",
|
|
@@ -11107,6 +11256,201 @@ var createVoiceCampaignRoutes = (options) => {
|
|
|
11107
11256
|
}
|
|
11108
11257
|
return app;
|
|
11109
11258
|
};
|
|
11259
|
+
// src/campaignDialers.ts
|
|
11260
|
+
import { Buffer as Buffer3 } from "buffer";
|
|
11261
|
+
var resolveFetch = (fetcher) => {
|
|
11262
|
+
const activeFetch = fetcher ?? globalThis.fetch;
|
|
11263
|
+
if (!activeFetch) {
|
|
11264
|
+
throw new Error("Campaign dialer requires fetch or globalThis.fetch.");
|
|
11265
|
+
}
|
|
11266
|
+
return activeFetch;
|
|
11267
|
+
};
|
|
11268
|
+
var resolveURL = async (resolver, input) => {
|
|
11269
|
+
if (!resolver) {
|
|
11270
|
+
return;
|
|
11271
|
+
}
|
|
11272
|
+
return typeof resolver === "function" ? await resolver(input) : resolver;
|
|
11273
|
+
};
|
|
11274
|
+
var baseCampaignMetadata = (input) => ({
|
|
11275
|
+
attemptId: input.attempt.id,
|
|
11276
|
+
campaignId: input.campaign.id,
|
|
11277
|
+
recipientId: input.recipient.id,
|
|
11278
|
+
voiceCampaignAttemptId: input.attempt.id,
|
|
11279
|
+
voiceCampaignId: input.campaign.id,
|
|
11280
|
+
voiceCampaignRecipientId: input.recipient.id
|
|
11281
|
+
});
|
|
11282
|
+
var resolveMetadata = async (metadata, input) => ({
|
|
11283
|
+
...baseCampaignMetadata(input),
|
|
11284
|
+
...typeof metadata === "function" ? await metadata(input) : metadata
|
|
11285
|
+
});
|
|
11286
|
+
var appendQuery = (url, params) => {
|
|
11287
|
+
if (!url) {
|
|
11288
|
+
return;
|
|
11289
|
+
}
|
|
11290
|
+
const parsed = new URL(url);
|
|
11291
|
+
for (const [key, value] of Object.entries(params)) {
|
|
11292
|
+
if (value !== undefined) {
|
|
11293
|
+
parsed.searchParams.set(key, String(value));
|
|
11294
|
+
}
|
|
11295
|
+
}
|
|
11296
|
+
return parsed.toString();
|
|
11297
|
+
};
|
|
11298
|
+
var readJson = async (response) => {
|
|
11299
|
+
const text = await response.text();
|
|
11300
|
+
if (!text.trim()) {
|
|
11301
|
+
return {};
|
|
11302
|
+
}
|
|
11303
|
+
try {
|
|
11304
|
+
return JSON.parse(text);
|
|
11305
|
+
} catch {
|
|
11306
|
+
return {
|
|
11307
|
+
text
|
|
11308
|
+
};
|
|
11309
|
+
}
|
|
11310
|
+
};
|
|
11311
|
+
var assertOk = async (response, provider) => {
|
|
11312
|
+
const body = await readJson(response);
|
|
11313
|
+
if (!response.ok) {
|
|
11314
|
+
throw new Error(`${provider} campaign dialer failed with ${response.status}: ${JSON.stringify(body)}`);
|
|
11315
|
+
}
|
|
11316
|
+
return body;
|
|
11317
|
+
};
|
|
11318
|
+
var firstString2 = (values) => {
|
|
11319
|
+
for (const value of values) {
|
|
11320
|
+
if (typeof value === "string" && value.trim()) {
|
|
11321
|
+
return value.trim();
|
|
11322
|
+
}
|
|
11323
|
+
}
|
|
11324
|
+
};
|
|
11325
|
+
var formBody = (values) => {
|
|
11326
|
+
const body = new URLSearchParams;
|
|
11327
|
+
for (const [key, value] of Object.entries(values)) {
|
|
11328
|
+
if (Array.isArray(value)) {
|
|
11329
|
+
for (const item of value) {
|
|
11330
|
+
if (item !== undefined) {
|
|
11331
|
+
body.append(key, String(item));
|
|
11332
|
+
}
|
|
11333
|
+
}
|
|
11334
|
+
continue;
|
|
11335
|
+
}
|
|
11336
|
+
if (value !== undefined) {
|
|
11337
|
+
body.set(key, String(value));
|
|
11338
|
+
}
|
|
11339
|
+
}
|
|
11340
|
+
return body;
|
|
11341
|
+
};
|
|
11342
|
+
var createVoiceTwilioCampaignDialer = (options) => async (input) => {
|
|
11343
|
+
const fetcher = resolveFetch(options.fetch);
|
|
11344
|
+
const metadata = await resolveMetadata(options.metadata, {
|
|
11345
|
+
...input,
|
|
11346
|
+
provider: "twilio"
|
|
11347
|
+
});
|
|
11348
|
+
const answerUrl = appendQuery(await resolveURL(options.answerUrl, input), metadata);
|
|
11349
|
+
const statusCallbackUrl = appendQuery(await resolveURL(options.statusCallbackUrl, input), metadata);
|
|
11350
|
+
const response = await fetcher(`${options.apiBaseUrl ?? "https://api.twilio.com"}/2010-04-01/Accounts/${encodeURIComponent(options.accountSid)}/Calls.json`, {
|
|
11351
|
+
body: formBody({
|
|
11352
|
+
From: options.from,
|
|
11353
|
+
MachineDetection: options.machineDetection,
|
|
11354
|
+
Method: options.answerMethod,
|
|
11355
|
+
StatusCallback: statusCallbackUrl,
|
|
11356
|
+
StatusCallbackEvent: options.statusCallbackEvents,
|
|
11357
|
+
StatusCallbackMethod: options.statusCallbackMethod,
|
|
11358
|
+
To: input.recipient.phone,
|
|
11359
|
+
Url: answerUrl
|
|
11360
|
+
}),
|
|
11361
|
+
headers: {
|
|
11362
|
+
authorization: `Basic ${Buffer3.from(`${options.accountSid}:${options.authToken}`).toString("base64")}`,
|
|
11363
|
+
"content-type": "application/x-www-form-urlencoded"
|
|
11364
|
+
},
|
|
11365
|
+
method: "POST"
|
|
11366
|
+
});
|
|
11367
|
+
const body = await assertOk(response, "Twilio");
|
|
11368
|
+
return {
|
|
11369
|
+
externalCallId: firstString2([body.sid, body.callSid, body.call_sid]),
|
|
11370
|
+
metadata: {
|
|
11371
|
+
provider: "twilio",
|
|
11372
|
+
response: body
|
|
11373
|
+
},
|
|
11374
|
+
status: "running"
|
|
11375
|
+
};
|
|
11376
|
+
};
|
|
11377
|
+
var createVoiceTelnyxCampaignDialer = (options) => async (input) => {
|
|
11378
|
+
const fetcher = resolveFetch(options.fetch);
|
|
11379
|
+
const metadata = await resolveMetadata(options.clientState, {
|
|
11380
|
+
...input,
|
|
11381
|
+
provider: "telnyx"
|
|
11382
|
+
});
|
|
11383
|
+
const webhookUrl = appendQuery(await resolveURL(options.webhookUrl, input), metadata);
|
|
11384
|
+
const response = await fetcher(`${options.apiBaseUrl ?? "https://api.telnyx.com"}/v2/calls`, {
|
|
11385
|
+
body: JSON.stringify({
|
|
11386
|
+
client_state: Buffer3.from(JSON.stringify(metadata)).toString("base64"),
|
|
11387
|
+
connection_id: options.connectionId,
|
|
11388
|
+
from: options.from,
|
|
11389
|
+
to: input.recipient.phone,
|
|
11390
|
+
webhook_url: webhookUrl,
|
|
11391
|
+
webhook_url_method: options.webhookUrlMethod
|
|
11392
|
+
}),
|
|
11393
|
+
headers: {
|
|
11394
|
+
authorization: `Bearer ${options.apiKey}`,
|
|
11395
|
+
"content-type": "application/json"
|
|
11396
|
+
},
|
|
11397
|
+
method: "POST"
|
|
11398
|
+
});
|
|
11399
|
+
const body = await assertOk(response, "Telnyx");
|
|
11400
|
+
const data = typeof body.data === "object" && body.data !== null ? body.data : body;
|
|
11401
|
+
return {
|
|
11402
|
+
externalCallId: firstString2([
|
|
11403
|
+
data.call_control_id,
|
|
11404
|
+
data.call_session_id,
|
|
11405
|
+
data.call_leg_id,
|
|
11406
|
+
body.call_control_id,
|
|
11407
|
+
body.call_session_id
|
|
11408
|
+
]),
|
|
11409
|
+
metadata: {
|
|
11410
|
+
provider: "telnyx",
|
|
11411
|
+
response: body
|
|
11412
|
+
},
|
|
11413
|
+
status: "running"
|
|
11414
|
+
};
|
|
11415
|
+
};
|
|
11416
|
+
var createVoicePlivoCampaignDialer = (options) => async (input) => {
|
|
11417
|
+
const fetcher = resolveFetch(options.fetch);
|
|
11418
|
+
const metadata = await resolveMetadata(options.metadata, {
|
|
11419
|
+
...input,
|
|
11420
|
+
provider: "plivo"
|
|
11421
|
+
});
|
|
11422
|
+
const answerUrl = appendQuery(await resolveURL(options.answerUrl, input), metadata);
|
|
11423
|
+
const callbackUrl = appendQuery(await resolveURL(options.callbackUrl, input), metadata);
|
|
11424
|
+
const response = await fetcher(`${options.apiBaseUrl ?? "https://api.plivo.com"}/v1/Account/${encodeURIComponent(options.authId)}/Call/`, {
|
|
11425
|
+
body: formBody({
|
|
11426
|
+
answer_method: options.answerMethod,
|
|
11427
|
+
answer_url: answerUrl,
|
|
11428
|
+
callback_method: options.callbackMethod,
|
|
11429
|
+
callback_url: callbackUrl,
|
|
11430
|
+
from: options.from,
|
|
11431
|
+
to: input.recipient.phone
|
|
11432
|
+
}),
|
|
11433
|
+
headers: {
|
|
11434
|
+
authorization: `Basic ${Buffer3.from(`${options.authId}:${options.authToken}`).toString("base64")}`,
|
|
11435
|
+
"content-type": "application/x-www-form-urlencoded"
|
|
11436
|
+
},
|
|
11437
|
+
method: "POST"
|
|
11438
|
+
});
|
|
11439
|
+
const body = await assertOk(response, "Plivo");
|
|
11440
|
+
return {
|
|
11441
|
+
externalCallId: firstString2([
|
|
11442
|
+
body.request_uuid,
|
|
11443
|
+
body.requestUuid,
|
|
11444
|
+
body.call_uuid,
|
|
11445
|
+
body.callUuid
|
|
11446
|
+
]),
|
|
11447
|
+
metadata: {
|
|
11448
|
+
provider: "plivo",
|
|
11449
|
+
response: body
|
|
11450
|
+
},
|
|
11451
|
+
status: "running"
|
|
11452
|
+
};
|
|
11453
|
+
};
|
|
11110
11454
|
// src/simulationSuite.ts
|
|
11111
11455
|
import { Elysia as Elysia20 } from "elysia";
|
|
11112
11456
|
|
|
@@ -12541,7 +12885,7 @@ var createMemoryVoiceTelephonyWebhookIdempotencyStore = () => {
|
|
|
12541
12885
|
};
|
|
12542
12886
|
};
|
|
12543
12887
|
var normalizeToken = (value) => typeof value === "string" ? value.trim().toLowerCase().replace(/\s+/g, "-").replace(/_+/g, "-") : undefined;
|
|
12544
|
-
var
|
|
12888
|
+
var firstString3 = (source, keys) => {
|
|
12545
12889
|
for (const key of keys) {
|
|
12546
12890
|
const value = source[key];
|
|
12547
12891
|
if (typeof value === "string" && value.trim()) {
|
|
@@ -12913,8 +13257,8 @@ var verifyVoiceTelephonyWebhook = async (input) => {
|
|
|
12913
13257
|
var durationMsFromSeconds = (value) => typeof value === "number" ? value * 1000 : undefined;
|
|
12914
13258
|
var parseVoiceTelephonyWebhookEvent = (input) => {
|
|
12915
13259
|
const payload = flattenPayload(input.body);
|
|
12916
|
-
const provider =
|
|
12917
|
-
const status =
|
|
13260
|
+
const provider = firstString3(payload, ["provider", "Provider"]) ?? input.provider;
|
|
13261
|
+
const status = firstString3(payload, [
|
|
12918
13262
|
"CallStatus",
|
|
12919
13263
|
"call_status",
|
|
12920
13264
|
"callStatus",
|
|
@@ -12939,9 +13283,9 @@ var parseVoiceTelephonyWebhookEvent = (input) => {
|
|
|
12939
13283
|
"sip_code",
|
|
12940
13284
|
"hangupCauseCode"
|
|
12941
13285
|
]);
|
|
12942
|
-
const from =
|
|
12943
|
-
const to =
|
|
12944
|
-
const target =
|
|
13286
|
+
const from = firstString3(payload, ["From", "from", "caller_id", "callerId"]);
|
|
13287
|
+
const to = firstString3(payload, ["To", "to", "called_number", "calledNumber"]);
|
|
13288
|
+
const target = firstString3(payload, [
|
|
12945
13289
|
"transferTarget",
|
|
12946
13290
|
"TransferTarget",
|
|
12947
13291
|
"target",
|
|
@@ -12949,7 +13293,7 @@ var parseVoiceTelephonyWebhookEvent = (input) => {
|
|
|
12949
13293
|
"department"
|
|
12950
13294
|
]);
|
|
12951
13295
|
return {
|
|
12952
|
-
answeredBy:
|
|
13296
|
+
answeredBy: firstString3(payload, [
|
|
12953
13297
|
"AnsweredBy",
|
|
12954
13298
|
"answered_by",
|
|
12955
13299
|
"answeredBy",
|
|
@@ -12958,9 +13302,12 @@ var parseVoiceTelephonyWebhookEvent = (input) => {
|
|
|
12958
13302
|
]),
|
|
12959
13303
|
durationMs,
|
|
12960
13304
|
from,
|
|
12961
|
-
metadata:
|
|
13305
|
+
metadata: {
|
|
13306
|
+
...input.query,
|
|
13307
|
+
...payload
|
|
13308
|
+
},
|
|
12962
13309
|
provider,
|
|
12963
|
-
reason:
|
|
13310
|
+
reason: firstString3(payload, [
|
|
12964
13311
|
"Reason",
|
|
12965
13312
|
"reason",
|
|
12966
13313
|
"HangupCause",
|
|
@@ -12976,7 +13323,7 @@ var parseVoiceTelephonyWebhookEvent = (input) => {
|
|
|
12976
13323
|
var defaultSessionId = (input) => {
|
|
12977
13324
|
const payload = flattenPayload(input.body);
|
|
12978
13325
|
const metadataSessionId = input.event.metadata?.sessionId;
|
|
12979
|
-
return
|
|
13326
|
+
return firstString3(input.query, ["sessionId", "session_id"]) ?? firstString3(payload, [
|
|
12980
13327
|
"sessionId",
|
|
12981
13328
|
"session_id",
|
|
12982
13329
|
"SessionId",
|
|
@@ -12991,7 +13338,7 @@ var defaultSessionId = (input) => {
|
|
|
12991
13338
|
};
|
|
12992
13339
|
var defaultIdempotencyKey = (input) => {
|
|
12993
13340
|
const payload = flattenPayload(input.body);
|
|
12994
|
-
const eventId =
|
|
13341
|
+
const eventId = firstString3(payload, [
|
|
12995
13342
|
"id",
|
|
12996
13343
|
"event_id",
|
|
12997
13344
|
"eventId",
|
|
@@ -13152,11 +13499,11 @@ var createVoiceTelephonyWebhookRoutes = (options = {}) => {
|
|
|
13152
13499
|
import { Elysia as Elysia28 } from "elysia";
|
|
13153
13500
|
|
|
13154
13501
|
// src/telephony/plivo.ts
|
|
13155
|
-
import { Buffer as
|
|
13502
|
+
import { Buffer as Buffer5 } from "buffer";
|
|
13156
13503
|
import { Elysia as Elysia26 } from "elysia";
|
|
13157
13504
|
|
|
13158
13505
|
// src/telephony/twilio.ts
|
|
13159
|
-
import { Buffer as
|
|
13506
|
+
import { Buffer as Buffer4 } from "buffer";
|
|
13160
13507
|
import { Elysia as Elysia25 } from "elysia";
|
|
13161
13508
|
var TWILIO_MULAW_SAMPLE_RATE = 8000;
|
|
13162
13509
|
var VOICE_PCM_SAMPLE_RATE = 16000;
|
|
@@ -13447,7 +13794,7 @@ var bytesToInt16Array = (bytes) => {
|
|
|
13447
13794
|
return output;
|
|
13448
13795
|
};
|
|
13449
13796
|
var decodeTwilioMulawBase64 = (payload) => {
|
|
13450
|
-
const bytes = Uint8Array.from(
|
|
13797
|
+
const bytes = Uint8Array.from(Buffer4.from(payload, "base64"));
|
|
13451
13798
|
const samples = new Int16Array(bytes.length);
|
|
13452
13799
|
for (let index = 0;index < bytes.length; index += 1) {
|
|
13453
13800
|
samples[index] = decodeMulawSample(bytes[index] ?? 0);
|
|
@@ -13459,7 +13806,7 @@ var encodeTwilioMulawBase64 = (samples) => {
|
|
|
13459
13806
|
for (let index = 0;index < samples.length; index += 1) {
|
|
13460
13807
|
bytes[index] = encodeMulawSample(samples[index] ?? 0);
|
|
13461
13808
|
}
|
|
13462
|
-
return
|
|
13809
|
+
return Buffer4.from(bytes).toString("base64");
|
|
13463
13810
|
};
|
|
13464
13811
|
var transcodeTwilioInboundPayloadToPCM16 = (payload) => {
|
|
13465
13812
|
const narrowband = decodeTwilioMulawBase64(payload);
|
|
@@ -13468,7 +13815,7 @@ var transcodeTwilioInboundPayloadToPCM16 = (payload) => {
|
|
|
13468
13815
|
};
|
|
13469
13816
|
var transcodePCMToTwilioOutboundPayload = (chunk, format) => {
|
|
13470
13817
|
if (format.container === "raw" && format.encoding === "mulaw" && format.channels === 1 && format.sampleRateHz === TWILIO_MULAW_SAMPLE_RATE) {
|
|
13471
|
-
return
|
|
13818
|
+
return Buffer4.from(chunk).toString("base64");
|
|
13472
13819
|
}
|
|
13473
13820
|
if (format.encoding !== "pcm_s16le") {
|
|
13474
13821
|
throw new Error(`Unsupported outbound telephony audio format: ${format.container}/${format.encoding}`);
|
|
@@ -13509,7 +13856,7 @@ var createTwilioSocketAdapter = (socket, getState) => ({
|
|
|
13509
13856
|
return;
|
|
13510
13857
|
}
|
|
13511
13858
|
if (message.type === "audio") {
|
|
13512
|
-
const payload = transcodePCMToTwilioOutboundPayload(Uint8Array.from(
|
|
13859
|
+
const payload = transcodePCMToTwilioOutboundPayload(Uint8Array.from(Buffer4.from(message.chunkBase64, "base64")), message.format);
|
|
13513
13860
|
state.hasOutboundAudioSinceLastInbound = true;
|
|
13514
13861
|
state.reviewRecorder?.recordTwilioOutbound({
|
|
13515
13862
|
bytes: payload.length,
|
|
@@ -14035,7 +14382,7 @@ var createPlivoMediaStreamBridge = (socket, options) => {
|
|
|
14035
14382
|
}
|
|
14036
14383
|
};
|
|
14037
14384
|
};
|
|
14038
|
-
var toBase642 = (bytes) =>
|
|
14385
|
+
var toBase642 = (bytes) => Buffer5.from(new Uint8Array(bytes)).toString("base64");
|
|
14039
14386
|
var timingSafeEqual2 = (left, right) => {
|
|
14040
14387
|
const encoder = new TextEncoder;
|
|
14041
14388
|
const leftBytes = encoder.encode(left);
|
|
@@ -14337,7 +14684,7 @@ var createPlivoVoiceRoutes = (options = {}) => {
|
|
|
14337
14684
|
};
|
|
14338
14685
|
|
|
14339
14686
|
// src/telephony/telnyx.ts
|
|
14340
|
-
import { Buffer as
|
|
14687
|
+
import { Buffer as Buffer6 } from "buffer";
|
|
14341
14688
|
import { Elysia as Elysia27 } from "elysia";
|
|
14342
14689
|
var escapeXml4 = (value) => value.replaceAll("&", "&").replaceAll('"', """).replaceAll("'", "'").replaceAll("<", "<").replaceAll(">", ">");
|
|
14343
14690
|
var escapeHtml27 = (value) => value.replaceAll("&", "&").replaceAll('"', """).replaceAll("'", "'").replaceAll("<", "<").replaceAll(">", ">");
|
|
@@ -14487,7 +14834,7 @@ var createTelnyxMediaStreamBridge = (socket, options) => {
|
|
|
14487
14834
|
}
|
|
14488
14835
|
};
|
|
14489
14836
|
};
|
|
14490
|
-
var decodeBase64 = (value) => Uint8Array.from(
|
|
14837
|
+
var decodeBase64 = (value) => Uint8Array.from(Buffer6.from(value, "base64"));
|
|
14491
14838
|
var verifyVoiceTelnyxWebhookSignature = async (input) => {
|
|
14492
14839
|
if (!input.publicKey) {
|
|
14493
14840
|
return { ok: false, reason: "missing-secret" };
|
|
@@ -19197,6 +19544,7 @@ export {
|
|
|
19197
19544
|
createVoiceWebhookDeliveryWorkerLoop,
|
|
19198
19545
|
createVoiceWebhookDeliveryWorker,
|
|
19199
19546
|
createVoiceTwilioRedirectHandoffAdapter,
|
|
19547
|
+
createVoiceTwilioCampaignDialer,
|
|
19200
19548
|
createVoiceTurnQualityRoutes,
|
|
19201
19549
|
createVoiceTurnQualityJSONHandler,
|
|
19202
19550
|
createVoiceTurnQualityHTMLHandler,
|
|
@@ -19219,6 +19567,7 @@ export {
|
|
|
19219
19567
|
createVoiceToolContractJSONHandler,
|
|
19220
19568
|
createVoiceToolContractHTMLHandler,
|
|
19221
19569
|
createVoiceToolContract,
|
|
19570
|
+
createVoiceTelnyxCampaignDialer,
|
|
19222
19571
|
createVoiceTelephonyWebhookRoutes,
|
|
19223
19572
|
createVoiceTelephonyWebhookHandler,
|
|
19224
19573
|
createVoiceTelephonyOutcomePolicy,
|
|
@@ -19275,6 +19624,7 @@ export {
|
|
|
19275
19624
|
createVoicePostgresIntegrationEventStore,
|
|
19276
19625
|
createVoicePostgresExternalObjectMapStore,
|
|
19277
19626
|
createVoicePostgresCampaignStore,
|
|
19627
|
+
createVoicePlivoCampaignDialer,
|
|
19278
19628
|
createVoicePhoneAgent,
|
|
19279
19629
|
createVoiceOutcomeContractRoutes,
|
|
19280
19630
|
createVoiceOutcomeContractJSONHandler,
|
|
@@ -19330,6 +19680,7 @@ export {
|
|
|
19330
19680
|
createVoiceDiagnosticsRoutes,
|
|
19331
19681
|
createVoiceCampaignWorkerLoop,
|
|
19332
19682
|
createVoiceCampaignWorker,
|
|
19683
|
+
createVoiceCampaignTelephonyOutcomeHandler,
|
|
19333
19684
|
createVoiceCampaignRoutes,
|
|
19334
19685
|
createVoiceCampaign,
|
|
19335
19686
|
createVoiceCallReviewRecorder,
|
|
@@ -19389,6 +19740,7 @@ export {
|
|
|
19389
19740
|
applyVoiceOpsTaskPolicy,
|
|
19390
19741
|
applyVoiceOpsTaskAssignmentRule,
|
|
19391
19742
|
applyVoiceHandoffDeliveryResult,
|
|
19743
|
+
applyVoiceCampaignTelephonyOutcome,
|
|
19392
19744
|
applyRiskTieredPhraseHintCorrections,
|
|
19393
19745
|
applyPhraseHintCorrections,
|
|
19394
19746
|
TURN_PROFILE_DEFAULTS
|
package/dist/testing/index.js
CHANGED
|
@@ -8434,7 +8434,10 @@ var parseVoiceTelephonyWebhookEvent = (input) => {
|
|
|
8434
8434
|
]),
|
|
8435
8435
|
durationMs,
|
|
8436
8436
|
from,
|
|
8437
|
-
metadata:
|
|
8437
|
+
metadata: {
|
|
8438
|
+
...input.query,
|
|
8439
|
+
...payload
|
|
8440
|
+
},
|
|
8438
8441
|
provider,
|
|
8439
8442
|
reason: firstString(payload, [
|
|
8440
8443
|
"Reason",
|