@absolutejs/voice 0.0.22-beta.80 → 0.0.22-beta.82
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/index.d.ts +4 -4
- package/dist/index.js +281 -0
- package/dist/telephony/plivo.d.ts +101 -1
- package/dist/telephony/telnyx.d.ts +109 -1
- package/dist/testing/index.js +2 -0
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -82,13 +82,13 @@ export type { VoiceSQLiteRuntimeStorage, VoiceSQLiteStoreOptions } from './sqlit
|
|
|
82
82
|
export type { StoredVoiceIntegrationEvent, StoredVoiceExternalObjectMap, StoredVoiceOpsTask, VoiceExternalObjectMap, VoiceExternalObjectMapStore, VoiceOpsTaskAgeBucket, VoiceOpsTaskAnalyticsOptions, VoiceOpsTaskAnalyticsSummary, VoiceOpsTaskAssignmentRule, VoiceOpsTaskAssignmentRuleCondition, VoiceOpsTaskAssignmentRules, VoiceOpsTaskAssigneeAnalytics, VoiceOpsDispositionTaskPolicies, VoiceOpsSLABreachPolicy, VoiceIntegrationDeliveryStatus, VoiceIntegrationEvent, VoiceIntegrationEventStore, VoiceIntegrationSinkDelivery, VoiceIntegrationEventType, VoiceIntegrationWebhookConfig, VoiceOpsTask, VoiceOpsTaskHistoryEntry, VoiceOpsTaskKind, VoiceOpsTaskPolicy, VoiceOpsTaskPriority, VoiceOpsTaskStatus, VoiceOpsTaskStore, VoiceOpsTaskSummary, VoiceOpsTaskWorkerAnalytics } from './ops';
|
|
83
83
|
export { createTwilioMediaStreamBridge, createTwilioVoiceRoutes, createTwilioVoiceResponse, decodeTwilioMulawBase64, encodeTwilioMulawBase64, transcodePCMToTwilioOutboundPayload, transcodeTwilioInboundPayloadToPCM16 } from './telephony/twilio';
|
|
84
84
|
export { evaluateVoiceTelephonyContract } from './telephony/contract';
|
|
85
|
-
export { createTelnyxVoiceResponse, createTelnyxVoiceRoutes, verifyVoiceTelnyxWebhookSignature } from './telephony/telnyx';
|
|
86
|
-
export { createPlivoVoiceResponse, createPlivoVoiceRoutes, signVoicePlivoWebhook, verifyVoicePlivoWebhookSignature } from './telephony/plivo';
|
|
85
|
+
export { createTelnyxMediaStreamBridge, createTelnyxVoiceResponse, createTelnyxVoiceRoutes, verifyVoiceTelnyxWebhookSignature } from './telephony/telnyx';
|
|
86
|
+
export { createPlivoMediaStreamBridge, createPlivoVoiceResponse, createPlivoVoiceRoutes, signVoicePlivoWebhook, verifyVoicePlivoWebhookSignature } from './telephony/plivo';
|
|
87
87
|
export { createVoiceTelephonyCarrierMatrix, createVoiceTelephonyCarrierMatrixRoutes, renderVoiceTelephonyCarrierMatrixHTML } from './telephony/matrix';
|
|
88
88
|
export type { TwilioInboundMessage, TwilioMediaStreamBridge, TwilioMediaStreamBridgeOptions, TwilioMediaStreamSocket, TwilioOutboundClearMessage, TwilioOutboundMarkMessage, TwilioOutboundMediaMessage, TwilioOutboundMessage, TwilioVoiceRouteParameters, TwilioVoiceResponseOptions, TwilioVoiceSmokeCheck, TwilioVoiceSmokeOptions, TwilioVoiceSmokeReport, TwilioVoiceSetupOptions, TwilioVoiceSetupStatus, TwilioVoiceRoutesOptions } from './telephony/twilio';
|
|
89
89
|
export type { VoiceTelephonyContractIssue, VoiceTelephonyContractOptions, VoiceTelephonyContractReport, VoiceTelephonyContractRequirement, VoiceTelephonyProvider, VoiceTelephonySetupStatus, VoiceTelephonySmokeCheck, VoiceTelephonySmokeReport } from './telephony/contract';
|
|
90
|
-
export type { TelnyxVoiceResponseOptions, TelnyxVoiceRoutesOptions, TelnyxVoiceSetupOptions, TelnyxVoiceSetupStatus, TelnyxVoiceSmokeCheck, TelnyxVoiceSmokeOptions, TelnyxVoiceSmokeReport } from './telephony/telnyx';
|
|
91
|
-
export type { PlivoVoiceResponseOptions, PlivoVoiceRoutesOptions, PlivoVoiceSetupOptions, PlivoVoiceSetupStatus, PlivoVoiceSmokeCheck, PlivoVoiceSmokeOptions, PlivoVoiceSmokeReport } from './telephony/plivo';
|
|
90
|
+
export type { TelnyxInboundMessage, TelnyxMediaPayload, TelnyxMediaStreamBridge, TelnyxMediaStreamBridgeOptions, TelnyxMediaStreamSocket, TelnyxOutboundClearMessage, TelnyxOutboundMarkMessage, TelnyxOutboundMediaMessage, TelnyxOutboundMessage, TelnyxVoiceResponseOptions, TelnyxVoiceRoutesOptions, TelnyxVoiceSetupOptions, TelnyxVoiceSetupStatus, TelnyxVoiceSmokeCheck, TelnyxVoiceSmokeOptions, TelnyxVoiceSmokeReport } from './telephony/telnyx';
|
|
91
|
+
export type { PlivoInboundMessage, PlivoMediaStreamBridge, PlivoMediaStreamBridgeOptions, PlivoMediaStreamSocket, PlivoOutboundCheckpointMessage, PlivoOutboundClearAudioMessage, PlivoOutboundMessage, PlivoOutboundPlayAudioMessage, PlivoVoiceResponseOptions, PlivoVoiceRoutesOptions, PlivoVoiceSetupOptions, PlivoVoiceSetupStatus, PlivoVoiceSmokeCheck, PlivoVoiceSmokeOptions, PlivoVoiceSmokeReport } from './telephony/plivo';
|
|
92
92
|
export type { VoiceTelephonyCarrierMatrix, VoiceTelephonyCarrierMatrixEntry, VoiceTelephonyCarrierMatrixInput, VoiceTelephonyCarrierMatrixOptions, VoiceTelephonyCarrierMatrixRoutesOptions, VoiceTelephonyCarrierMatrixStatus } from './telephony/matrix';
|
|
93
93
|
export { shapeTelephonyAssistantText } from './telephony/response';
|
|
94
94
|
export type { TelephonyResponseShapeMode, TelephonyResponseShapeOptions } from './telephony/response';
|
package/dist/index.js
CHANGED
|
@@ -11223,6 +11223,8 @@ var createVoiceTelephonyWebhookRoutes = (options = {}) => {
|
|
|
11223
11223
|
}
|
|
11224
11224
|
throw error;
|
|
11225
11225
|
}
|
|
11226
|
+
}, {
|
|
11227
|
+
parse: "none"
|
|
11226
11228
|
});
|
|
11227
11229
|
};
|
|
11228
11230
|
// src/fileStore.ts
|
|
@@ -16026,6 +16028,114 @@ var createTelnyxVoiceResponse = (options) => {
|
|
|
16026
16028
|
].filter((value) => Boolean(value)).join(" ");
|
|
16027
16029
|
return `<?xml version="1.0" encoding="UTF-8"?><Response><Start><Stream ${attributes} /></Start></Response>`;
|
|
16028
16030
|
};
|
|
16031
|
+
var parseTelnyxMessage = (raw) => {
|
|
16032
|
+
if (typeof raw !== "string") {
|
|
16033
|
+
return raw;
|
|
16034
|
+
}
|
|
16035
|
+
return JSON.parse(raw);
|
|
16036
|
+
};
|
|
16037
|
+
var normalizeTelnyxTrack = (track) => track === "outbound" || track === "outbound_track" ? "outbound" : "inbound";
|
|
16038
|
+
var telnyxToTwilioMessage = (message) => {
|
|
16039
|
+
switch (message.event) {
|
|
16040
|
+
case "connected":
|
|
16041
|
+
return {
|
|
16042
|
+
event: "connected",
|
|
16043
|
+
version: message.version
|
|
16044
|
+
};
|
|
16045
|
+
case "start": {
|
|
16046
|
+
const streamSid = message.stream_id ?? "telnyx-stream";
|
|
16047
|
+
return {
|
|
16048
|
+
event: "start",
|
|
16049
|
+
start: {
|
|
16050
|
+
callSid: message.start?.call_control_id ?? message.start?.call_session_id ?? message.start?.call_leg_id,
|
|
16051
|
+
customParameters: {
|
|
16052
|
+
...message.start?.custom_parameters ?? {},
|
|
16053
|
+
...message.start?.call_session_id ? { sessionId: message.start.call_session_id } : {}
|
|
16054
|
+
},
|
|
16055
|
+
mediaFormat: {
|
|
16056
|
+
channels: message.start?.media_format?.channels,
|
|
16057
|
+
encoding: message.start?.media_format?.encoding,
|
|
16058
|
+
sampleRate: message.start?.media_format?.sample_rate
|
|
16059
|
+
},
|
|
16060
|
+
streamSid
|
|
16061
|
+
},
|
|
16062
|
+
streamSid
|
|
16063
|
+
};
|
|
16064
|
+
}
|
|
16065
|
+
case "media": {
|
|
16066
|
+
const streamSid = message.stream_id ?? "telnyx-stream";
|
|
16067
|
+
return {
|
|
16068
|
+
event: "media",
|
|
16069
|
+
media: {
|
|
16070
|
+
chunk: message.media.chunk,
|
|
16071
|
+
payload: message.media.payload,
|
|
16072
|
+
timestamp: message.media.timestamp,
|
|
16073
|
+
track: normalizeTelnyxTrack(message.media.track)
|
|
16074
|
+
},
|
|
16075
|
+
streamSid
|
|
16076
|
+
};
|
|
16077
|
+
}
|
|
16078
|
+
case "mark":
|
|
16079
|
+
return {
|
|
16080
|
+
event: "mark",
|
|
16081
|
+
mark: message.mark,
|
|
16082
|
+
streamSid: message.stream_id ?? "telnyx-stream"
|
|
16083
|
+
};
|
|
16084
|
+
case "stop":
|
|
16085
|
+
return {
|
|
16086
|
+
event: "stop",
|
|
16087
|
+
stop: {
|
|
16088
|
+
callSid: message.stop?.call_control_id
|
|
16089
|
+
},
|
|
16090
|
+
streamSid: message.stream_id ?? "telnyx-stream"
|
|
16091
|
+
};
|
|
16092
|
+
case "dtmf":
|
|
16093
|
+
case "error":
|
|
16094
|
+
return null;
|
|
16095
|
+
}
|
|
16096
|
+
};
|
|
16097
|
+
var createTelnyxTwilioSocketAdapter = (socket) => ({
|
|
16098
|
+
close: (code, reason) => socket.close(code, reason),
|
|
16099
|
+
send: async (data) => {
|
|
16100
|
+
const message = JSON.parse(data);
|
|
16101
|
+
const telnyxMessage = message.event === "media" ? {
|
|
16102
|
+
event: "media",
|
|
16103
|
+
media: {
|
|
16104
|
+
payload: message.media.payload
|
|
16105
|
+
}
|
|
16106
|
+
} : message.event === "clear" ? {
|
|
16107
|
+
event: "clear"
|
|
16108
|
+
} : message.event === "mark" ? {
|
|
16109
|
+
event: "mark",
|
|
16110
|
+
mark: message.mark
|
|
16111
|
+
} : null;
|
|
16112
|
+
if (telnyxMessage) {
|
|
16113
|
+
await Promise.resolve(socket.send(JSON.stringify(telnyxMessage)));
|
|
16114
|
+
}
|
|
16115
|
+
}
|
|
16116
|
+
});
|
|
16117
|
+
var createTelnyxMediaStreamBridge = (socket, options) => {
|
|
16118
|
+
const bridge = createTwilioMediaStreamBridge(createTelnyxTwilioSocketAdapter(socket), {
|
|
16119
|
+
...options,
|
|
16120
|
+
onVoiceMessage: options.onVoiceMessage ? (input) => options.onVoiceMessage?.({
|
|
16121
|
+
callControlId: input.callSid,
|
|
16122
|
+
message: input.message,
|
|
16123
|
+
sessionId: input.sessionId,
|
|
16124
|
+
streamId: input.streamSid
|
|
16125
|
+
}) : undefined
|
|
16126
|
+
});
|
|
16127
|
+
return {
|
|
16128
|
+
close: bridge.close,
|
|
16129
|
+
getSessionId: bridge.getSessionId,
|
|
16130
|
+
getStreamId: bridge.getStreamSid,
|
|
16131
|
+
handleMessage: async (raw) => {
|
|
16132
|
+
const message = telnyxToTwilioMessage(parseTelnyxMessage(raw));
|
|
16133
|
+
if (message) {
|
|
16134
|
+
await bridge.handleMessage(message);
|
|
16135
|
+
}
|
|
16136
|
+
}
|
|
16137
|
+
};
|
|
16138
|
+
};
|
|
16029
16139
|
var decodeBase64 = (value) => Uint8Array.from(Buffer4.from(value, "base64"));
|
|
16030
16140
|
var verifyVoiceTelnyxWebhookSignature = async (input) => {
|
|
16031
16141
|
if (!input.publicKey) {
|
|
@@ -16180,6 +16290,7 @@ var createTelnyxVoiceRoutes = (options = {}) => {
|
|
|
16180
16290
|
const webhookPath = options.webhook?.path ?? "/api/voice/telnyx/webhook";
|
|
16181
16291
|
const setupPath = options.setup?.path === false ? false : options.setup?.path ?? "/api/voice/telnyx/setup";
|
|
16182
16292
|
const smokePath = options.smoke?.path === false ? false : options.smoke?.path ?? "/api/voice/telnyx/smoke";
|
|
16293
|
+
const bridges = new WeakMap;
|
|
16183
16294
|
const webhookPolicy = options.webhook?.policy ?? options.outcomePolicy ?? createVoiceTelephonyOutcomePolicy();
|
|
16184
16295
|
const verify = options.webhook?.verify ?? (options.webhook?.publicKey ? (input) => verifyVoiceTelnyxWebhookSignature({
|
|
16185
16296
|
body: input.rawBody,
|
|
@@ -16217,6 +16328,31 @@ var createTelnyxVoiceRoutes = (options = {}) => {
|
|
|
16217
16328
|
"content-type": "text/xml; charset=utf-8"
|
|
16218
16329
|
}
|
|
16219
16330
|
});
|
|
16331
|
+
}).ws(streamPath, {
|
|
16332
|
+
close: async (ws, _code, reason) => {
|
|
16333
|
+
const bridge = bridges.get(ws);
|
|
16334
|
+
bridges.delete(ws);
|
|
16335
|
+
await bridge?.close(reason);
|
|
16336
|
+
},
|
|
16337
|
+
message: async (ws, raw) => {
|
|
16338
|
+
if (!options.bridge) {
|
|
16339
|
+
ws.close(1011, "Telnyx media bridge is not configured.");
|
|
16340
|
+
return;
|
|
16341
|
+
}
|
|
16342
|
+
let bridge = bridges.get(ws);
|
|
16343
|
+
if (!bridge) {
|
|
16344
|
+
bridge = createTelnyxMediaStreamBridge({
|
|
16345
|
+
close: (code, reason) => {
|
|
16346
|
+
ws.close(code, reason);
|
|
16347
|
+
},
|
|
16348
|
+
send: (data) => {
|
|
16349
|
+
ws.send(data);
|
|
16350
|
+
}
|
|
16351
|
+
}, options.bridge);
|
|
16352
|
+
bridges.set(ws, bridge);
|
|
16353
|
+
}
|
|
16354
|
+
await bridge.handleMessage(raw);
|
|
16355
|
+
}
|
|
16220
16356
|
}).use(createVoiceTelephonyWebhookRoutes({
|
|
16221
16357
|
...options.webhook ?? {},
|
|
16222
16358
|
context: options.context,
|
|
@@ -16325,6 +16461,123 @@ var createPlivoVoiceResponse = (options) => {
|
|
|
16325
16461
|
const openTag = attributes ? `<Stream ${attributes}>` : "<Stream>";
|
|
16326
16462
|
return `<?xml version="1.0" encoding="UTF-8"?><Response>${openTag}${escapeXml4(options.streamUrl)}</Stream></Response>`;
|
|
16327
16463
|
};
|
|
16464
|
+
var parsePlivoMessage = (raw) => {
|
|
16465
|
+
if (typeof raw !== "string") {
|
|
16466
|
+
return raw;
|
|
16467
|
+
}
|
|
16468
|
+
return JSON.parse(raw);
|
|
16469
|
+
};
|
|
16470
|
+
var parsePlivoExtraHeaders = (headers) => {
|
|
16471
|
+
if (!headers) {
|
|
16472
|
+
return {};
|
|
16473
|
+
}
|
|
16474
|
+
return Object.fromEntries(headers.split(/[;,]/).map((header) => header.trim()).filter(Boolean).map((header) => {
|
|
16475
|
+
const separator = header.indexOf("=");
|
|
16476
|
+
if (separator === -1) {
|
|
16477
|
+
return [header, ""];
|
|
16478
|
+
}
|
|
16479
|
+
return [
|
|
16480
|
+
header.slice(0, separator).trim(),
|
|
16481
|
+
header.slice(separator + 1).trim()
|
|
16482
|
+
];
|
|
16483
|
+
}).filter((entry) => (entry[0] ?? "").length > 0));
|
|
16484
|
+
};
|
|
16485
|
+
var plivoToTwilioMessage = (message) => {
|
|
16486
|
+
switch (message.event) {
|
|
16487
|
+
case "start": {
|
|
16488
|
+
const streamSid = message.streamId ?? message.start?.streamId ?? "plivo-stream";
|
|
16489
|
+
const callSid = message.start?.callId ?? message.start?.callUuid;
|
|
16490
|
+
const customParameters = parsePlivoExtraHeaders(message.start?.extra_headers);
|
|
16491
|
+
return {
|
|
16492
|
+
event: "start",
|
|
16493
|
+
start: {
|
|
16494
|
+
callSid,
|
|
16495
|
+
customParameters,
|
|
16496
|
+
mediaFormat: message.start?.mediaFormat,
|
|
16497
|
+
streamSid
|
|
16498
|
+
},
|
|
16499
|
+
streamSid
|
|
16500
|
+
};
|
|
16501
|
+
}
|
|
16502
|
+
case "media": {
|
|
16503
|
+
const streamSid = message.streamId ?? "plivo-stream";
|
|
16504
|
+
return {
|
|
16505
|
+
event: "media",
|
|
16506
|
+
media: {
|
|
16507
|
+
payload: message.media.payload,
|
|
16508
|
+
timestamp: message.media.timestamp,
|
|
16509
|
+
track: message.media.track ?? "inbound"
|
|
16510
|
+
},
|
|
16511
|
+
streamSid
|
|
16512
|
+
};
|
|
16513
|
+
}
|
|
16514
|
+
case "playedStream":
|
|
16515
|
+
return {
|
|
16516
|
+
event: "mark",
|
|
16517
|
+
mark: {
|
|
16518
|
+
name: message.name
|
|
16519
|
+
},
|
|
16520
|
+
streamSid: message.streamId ?? "plivo-stream"
|
|
16521
|
+
};
|
|
16522
|
+
case "stop":
|
|
16523
|
+
return {
|
|
16524
|
+
event: "stop",
|
|
16525
|
+
stop: {
|
|
16526
|
+
callSid: message.stop?.callId ?? message.stop?.callUuid
|
|
16527
|
+
},
|
|
16528
|
+
streamSid: message.streamId ?? "plivo-stream"
|
|
16529
|
+
};
|
|
16530
|
+
case "clearedAudio":
|
|
16531
|
+
case "dtmf":
|
|
16532
|
+
return null;
|
|
16533
|
+
}
|
|
16534
|
+
};
|
|
16535
|
+
var createPlivoTwilioSocketAdapter = (socket) => ({
|
|
16536
|
+
close: (code, reason) => socket.close(code, reason),
|
|
16537
|
+
send: async (data) => {
|
|
16538
|
+
const message = JSON.parse(data);
|
|
16539
|
+
const plivoMessage = message.event === "media" ? {
|
|
16540
|
+
event: "playAudio",
|
|
16541
|
+
media: {
|
|
16542
|
+
contentType: "audio/x-mulaw",
|
|
16543
|
+
payload: message.media.payload,
|
|
16544
|
+
sampleRate: 8000
|
|
16545
|
+
}
|
|
16546
|
+
} : message.event === "clear" ? {
|
|
16547
|
+
event: "clearAudio",
|
|
16548
|
+
streamId: message.streamSid
|
|
16549
|
+
} : message.event === "mark" ? {
|
|
16550
|
+
event: "checkpoint",
|
|
16551
|
+
name: message.mark.name,
|
|
16552
|
+
streamId: message.streamSid
|
|
16553
|
+
} : null;
|
|
16554
|
+
if (plivoMessage) {
|
|
16555
|
+
await Promise.resolve(socket.send(JSON.stringify(plivoMessage)));
|
|
16556
|
+
}
|
|
16557
|
+
}
|
|
16558
|
+
});
|
|
16559
|
+
var createPlivoMediaStreamBridge = (socket, options) => {
|
|
16560
|
+
const bridge = createTwilioMediaStreamBridge(createPlivoTwilioSocketAdapter(socket), {
|
|
16561
|
+
...options,
|
|
16562
|
+
onVoiceMessage: options.onVoiceMessage ? (input) => options.onVoiceMessage?.({
|
|
16563
|
+
callId: input.callSid,
|
|
16564
|
+
message: input.message,
|
|
16565
|
+
sessionId: input.sessionId,
|
|
16566
|
+
streamId: input.streamSid
|
|
16567
|
+
}) : undefined
|
|
16568
|
+
});
|
|
16569
|
+
return {
|
|
16570
|
+
close: bridge.close,
|
|
16571
|
+
getSessionId: bridge.getSessionId,
|
|
16572
|
+
getStreamId: bridge.getStreamSid,
|
|
16573
|
+
handleMessage: async (raw) => {
|
|
16574
|
+
const message = plivoToTwilioMessage(parsePlivoMessage(raw));
|
|
16575
|
+
if (message) {
|
|
16576
|
+
await bridge.handleMessage(message);
|
|
16577
|
+
}
|
|
16578
|
+
}
|
|
16579
|
+
};
|
|
16580
|
+
};
|
|
16328
16581
|
var toBase642 = (bytes) => Buffer5.from(new Uint8Array(bytes)).toString("base64");
|
|
16329
16582
|
var timingSafeEqual3 = (left, right) => {
|
|
16330
16583
|
const encoder = new TextEncoder;
|
|
@@ -16505,6 +16758,7 @@ var createPlivoVoiceRoutes = (options = {}) => {
|
|
|
16505
16758
|
const webhookPath = options.webhook?.path ?? "/api/voice/plivo/webhook";
|
|
16506
16759
|
const setupPath = options.setup?.path === false ? false : options.setup?.path ?? "/api/voice/plivo/setup";
|
|
16507
16760
|
const smokePath = options.smoke?.path === false ? false : options.smoke?.path ?? "/api/voice/plivo/smoke";
|
|
16761
|
+
const bridges = new WeakMap;
|
|
16508
16762
|
const webhookPolicy = options.webhook?.policy ?? options.outcomePolicy ?? createVoiceTelephonyOutcomePolicy();
|
|
16509
16763
|
const verificationUrl = options.webhook?.verificationUrl;
|
|
16510
16764
|
const verify = options.webhook?.verify ?? (options.webhook?.authToken ? (input) => verifyVoicePlivoWebhookSignature({
|
|
@@ -16546,6 +16800,31 @@ var createPlivoVoiceRoutes = (options = {}) => {
|
|
|
16546
16800
|
"content-type": "text/xml; charset=utf-8"
|
|
16547
16801
|
}
|
|
16548
16802
|
});
|
|
16803
|
+
}).ws(streamPath, {
|
|
16804
|
+
close: async (ws, _code, reason) => {
|
|
16805
|
+
const bridge = bridges.get(ws);
|
|
16806
|
+
bridges.delete(ws);
|
|
16807
|
+
await bridge?.close(reason);
|
|
16808
|
+
},
|
|
16809
|
+
message: async (ws, raw) => {
|
|
16810
|
+
if (!options.bridge) {
|
|
16811
|
+
ws.close(1011, "Plivo media bridge is not configured.");
|
|
16812
|
+
return;
|
|
16813
|
+
}
|
|
16814
|
+
let bridge = bridges.get(ws);
|
|
16815
|
+
if (!bridge) {
|
|
16816
|
+
bridge = createPlivoMediaStreamBridge({
|
|
16817
|
+
close: (code, reason) => {
|
|
16818
|
+
ws.close(code, reason);
|
|
16819
|
+
},
|
|
16820
|
+
send: (data) => {
|
|
16821
|
+
ws.send(data);
|
|
16822
|
+
}
|
|
16823
|
+
}, options.bridge);
|
|
16824
|
+
bridges.set(ws, bridge);
|
|
16825
|
+
}
|
|
16826
|
+
await bridge.handleMessage(raw);
|
|
16827
|
+
}
|
|
16549
16828
|
}).use(createVoiceTelephonyWebhookRoutes({
|
|
16550
16829
|
...options.webhook ?? {},
|
|
16551
16830
|
context: options.context,
|
|
@@ -17011,6 +17290,7 @@ export {
|
|
|
17011
17290
|
createTwilioMediaStreamBridge,
|
|
17012
17291
|
createTelnyxVoiceRoutes,
|
|
17013
17292
|
createTelnyxVoiceResponse,
|
|
17293
|
+
createTelnyxMediaStreamBridge,
|
|
17014
17294
|
createStoredVoiceOpsTask,
|
|
17015
17295
|
createStoredVoiceIntegrationEvent,
|
|
17016
17296
|
createStoredVoiceExternalObjectMap,
|
|
@@ -17018,6 +17298,7 @@ export {
|
|
|
17018
17298
|
createRiskyTurnCorrectionHandler,
|
|
17019
17299
|
createPlivoVoiceRoutes,
|
|
17020
17300
|
createPlivoVoiceResponse,
|
|
17301
|
+
createPlivoMediaStreamBridge,
|
|
17021
17302
|
createPhraseHintCorrectionHandler,
|
|
17022
17303
|
createOpenAIVoiceAssistantModel,
|
|
17023
17304
|
createMemoryVoiceTelephonyWebhookIdempotencyStore,
|
|
@@ -1,7 +1,95 @@
|
|
|
1
1
|
import { Elysia } from 'elysia';
|
|
2
2
|
import { type VoiceTelephonyContractReport, type VoiceTelephonySetupStatus, type VoiceTelephonySmokeCheck, type VoiceTelephonySmokeReport } from './contract';
|
|
3
3
|
import { type VoiceTelephonyOutcomePolicy, type VoiceTelephonyWebhookRoutesOptions, type VoiceTelephonyWebhookVerificationResult } from '../telephonyOutcome';
|
|
4
|
-
import type { VoiceSessionRecord } from '../types';
|
|
4
|
+
import type { VoiceServerMessage, VoiceSessionRecord } from '../types';
|
|
5
|
+
import { type TwilioMediaStreamBridgeOptions } from './twilio';
|
|
6
|
+
export type PlivoInboundMessage = {
|
|
7
|
+
event: 'start';
|
|
8
|
+
sequenceNumber?: number;
|
|
9
|
+
start?: {
|
|
10
|
+
callId?: string;
|
|
11
|
+
callUuid?: string;
|
|
12
|
+
extra_headers?: string;
|
|
13
|
+
mediaFormat?: {
|
|
14
|
+
channels?: number;
|
|
15
|
+
encoding?: string;
|
|
16
|
+
sampleRate?: number;
|
|
17
|
+
};
|
|
18
|
+
streamId?: string;
|
|
19
|
+
};
|
|
20
|
+
streamId?: string;
|
|
21
|
+
} | {
|
|
22
|
+
event: 'media';
|
|
23
|
+
media: {
|
|
24
|
+
payload: string;
|
|
25
|
+
timestamp?: string;
|
|
26
|
+
track?: 'inbound' | 'outbound';
|
|
27
|
+
};
|
|
28
|
+
sequenceNumber?: number;
|
|
29
|
+
streamId?: string;
|
|
30
|
+
} | {
|
|
31
|
+
event: 'dtmf';
|
|
32
|
+
dtmf?: {
|
|
33
|
+
digit?: string;
|
|
34
|
+
timestamp?: string;
|
|
35
|
+
track?: string;
|
|
36
|
+
};
|
|
37
|
+
sequenceNumber?: number;
|
|
38
|
+
streamId?: string;
|
|
39
|
+
} | {
|
|
40
|
+
event: 'playedStream';
|
|
41
|
+
name?: string;
|
|
42
|
+
sequenceNumber?: number;
|
|
43
|
+
streamId?: string;
|
|
44
|
+
} | {
|
|
45
|
+
event: 'clearedAudio';
|
|
46
|
+
sequenceNumber?: number;
|
|
47
|
+
streamId?: string;
|
|
48
|
+
} | {
|
|
49
|
+
event: 'stop';
|
|
50
|
+
sequenceNumber?: number;
|
|
51
|
+
stop?: {
|
|
52
|
+
callId?: string;
|
|
53
|
+
callUuid?: string;
|
|
54
|
+
};
|
|
55
|
+
streamId?: string;
|
|
56
|
+
};
|
|
57
|
+
export type PlivoOutboundPlayAudioMessage = {
|
|
58
|
+
event: 'playAudio';
|
|
59
|
+
media: {
|
|
60
|
+
contentType: 'audio/x-mulaw';
|
|
61
|
+
payload: string;
|
|
62
|
+
sampleRate: 8000;
|
|
63
|
+
};
|
|
64
|
+
};
|
|
65
|
+
export type PlivoOutboundClearAudioMessage = {
|
|
66
|
+
event: 'clearAudio';
|
|
67
|
+
streamId?: string;
|
|
68
|
+
};
|
|
69
|
+
export type PlivoOutboundCheckpointMessage = {
|
|
70
|
+
event: 'checkpoint';
|
|
71
|
+
name: string;
|
|
72
|
+
streamId?: string;
|
|
73
|
+
};
|
|
74
|
+
export type PlivoOutboundMessage = PlivoOutboundPlayAudioMessage | PlivoOutboundClearAudioMessage | PlivoOutboundCheckpointMessage;
|
|
75
|
+
export type PlivoMediaStreamSocket = {
|
|
76
|
+
close: (code?: number, reason?: string) => void | Promise<void>;
|
|
77
|
+
send: (data: string) => void | Promise<void>;
|
|
78
|
+
};
|
|
79
|
+
export type PlivoMediaStreamBridgeOptions<TContext = unknown, TSession extends VoiceSessionRecord = VoiceSessionRecord, TResult = unknown> = Omit<TwilioMediaStreamBridgeOptions<TContext, TSession, TResult>, 'onVoiceMessage'> & {
|
|
80
|
+
onVoiceMessage?: (input: {
|
|
81
|
+
callId?: string;
|
|
82
|
+
message: VoiceServerMessage<TResult>;
|
|
83
|
+
sessionId: string;
|
|
84
|
+
streamId?: string;
|
|
85
|
+
}) => Promise<void> | void;
|
|
86
|
+
};
|
|
87
|
+
export type PlivoMediaStreamBridge = {
|
|
88
|
+
close: (reason?: string) => Promise<void>;
|
|
89
|
+
getSessionId: () => string | null;
|
|
90
|
+
getStreamId: () => string | null;
|
|
91
|
+
handleMessage: (raw: string | PlivoInboundMessage) => Promise<void>;
|
|
92
|
+
};
|
|
5
93
|
export type PlivoVoiceResponseOptions = {
|
|
6
94
|
audioTrack?: 'both' | 'inbound' | 'outbound';
|
|
7
95
|
bidirectional?: boolean;
|
|
@@ -53,6 +141,7 @@ export type PlivoVoiceRoutesOptions<TContext = unknown, TSession extends VoiceSe
|
|
|
53
141
|
streamPath: string;
|
|
54
142
|
}) => Promise<string> | string);
|
|
55
143
|
};
|
|
144
|
+
bridge?: PlivoMediaStreamBridgeOptions<TContext, TSession, TResult>;
|
|
56
145
|
context?: TContext;
|
|
57
146
|
name?: string;
|
|
58
147
|
outcomePolicy?: VoiceTelephonyOutcomePolicy;
|
|
@@ -70,6 +159,7 @@ export type PlivoVoiceRoutesOptions<TContext = unknown, TSession extends VoiceSe
|
|
|
70
159
|
};
|
|
71
160
|
};
|
|
72
161
|
export declare const createPlivoVoiceResponse: (options: PlivoVoiceResponseOptions) => string;
|
|
162
|
+
export declare const createPlivoMediaStreamBridge: <TContext = unknown, TSession extends VoiceSessionRecord = VoiceSessionRecord, TResult = unknown>(socket: PlivoMediaStreamSocket, options: PlivoMediaStreamBridgeOptions<TContext, TSession, TResult>) => PlivoMediaStreamBridge;
|
|
73
163
|
export declare const signVoicePlivoWebhook: (input: {
|
|
74
164
|
authToken: string;
|
|
75
165
|
body?: unknown;
|
|
@@ -121,6 +211,16 @@ export declare const createPlivoVoiceRoutes: <TContext = unknown, TSession exten
|
|
|
121
211
|
};
|
|
122
212
|
};
|
|
123
213
|
};
|
|
214
|
+
} & {
|
|
215
|
+
[x: string]: {
|
|
216
|
+
subscribe: {
|
|
217
|
+
body: unknown;
|
|
218
|
+
params: {};
|
|
219
|
+
query: unknown;
|
|
220
|
+
headers: unknown;
|
|
221
|
+
response: {};
|
|
222
|
+
};
|
|
223
|
+
};
|
|
124
224
|
} & {
|
|
125
225
|
[x: string]: {
|
|
126
226
|
post: {
|
|
@@ -1,7 +1,103 @@
|
|
|
1
1
|
import { Elysia } from 'elysia';
|
|
2
2
|
import { type VoiceTelephonyContractReport, type VoiceTelephonySetupStatus, type VoiceTelephonySmokeCheck, type VoiceTelephonySmokeReport } from './contract';
|
|
3
3
|
import { type VoiceTelephonyOutcomePolicy, type VoiceTelephonyWebhookRoutesOptions, type VoiceTelephonyWebhookVerificationResult } from '../telephonyOutcome';
|
|
4
|
-
import type { VoiceSessionRecord } from '../types';
|
|
4
|
+
import type { VoiceServerMessage, VoiceSessionRecord } from '../types';
|
|
5
|
+
import { type TwilioMediaStreamBridgeOptions } from './twilio';
|
|
6
|
+
export type TelnyxMediaPayload = {
|
|
7
|
+
chunk?: string;
|
|
8
|
+
payload: string;
|
|
9
|
+
timestamp?: string;
|
|
10
|
+
track?: 'inbound' | 'outbound' | 'inbound_track' | 'outbound_track';
|
|
11
|
+
};
|
|
12
|
+
export type TelnyxInboundMessage = {
|
|
13
|
+
event: 'connected';
|
|
14
|
+
version?: string;
|
|
15
|
+
} | {
|
|
16
|
+
event: 'start';
|
|
17
|
+
sequence_number?: string;
|
|
18
|
+
start?: {
|
|
19
|
+
call_control_id?: string;
|
|
20
|
+
call_leg_id?: string;
|
|
21
|
+
call_session_id?: string;
|
|
22
|
+
custom_parameters?: Record<string, string>;
|
|
23
|
+
media_format?: {
|
|
24
|
+
channels?: number;
|
|
25
|
+
encoding?: string;
|
|
26
|
+
sample_rate?: number;
|
|
27
|
+
};
|
|
28
|
+
user_id?: string;
|
|
29
|
+
};
|
|
30
|
+
stream_id?: string;
|
|
31
|
+
} | {
|
|
32
|
+
event: 'media';
|
|
33
|
+
media: TelnyxMediaPayload;
|
|
34
|
+
sequence_number?: string;
|
|
35
|
+
stream_id?: string;
|
|
36
|
+
} | {
|
|
37
|
+
event: 'mark';
|
|
38
|
+
mark?: {
|
|
39
|
+
name?: string;
|
|
40
|
+
};
|
|
41
|
+
sequence_number?: string;
|
|
42
|
+
stream_id?: string;
|
|
43
|
+
} | {
|
|
44
|
+
event: 'dtmf';
|
|
45
|
+
dtmf?: {
|
|
46
|
+
digit?: string;
|
|
47
|
+
};
|
|
48
|
+
sequence_number?: string;
|
|
49
|
+
stream_id?: string;
|
|
50
|
+
} | {
|
|
51
|
+
event: 'error';
|
|
52
|
+
payload?: {
|
|
53
|
+
code?: number;
|
|
54
|
+
detail?: string;
|
|
55
|
+
title?: string;
|
|
56
|
+
};
|
|
57
|
+
stream_id?: string;
|
|
58
|
+
} | {
|
|
59
|
+
event: 'stop';
|
|
60
|
+
sequence_number?: string;
|
|
61
|
+
stop?: {
|
|
62
|
+
call_control_id?: string;
|
|
63
|
+
user_id?: string;
|
|
64
|
+
};
|
|
65
|
+
stream_id?: string;
|
|
66
|
+
};
|
|
67
|
+
export type TelnyxOutboundMediaMessage = {
|
|
68
|
+
event: 'media';
|
|
69
|
+
media: {
|
|
70
|
+
payload: string;
|
|
71
|
+
};
|
|
72
|
+
};
|
|
73
|
+
export type TelnyxOutboundClearMessage = {
|
|
74
|
+
event: 'clear';
|
|
75
|
+
};
|
|
76
|
+
export type TelnyxOutboundMarkMessage = {
|
|
77
|
+
event: 'mark';
|
|
78
|
+
mark: {
|
|
79
|
+
name: string;
|
|
80
|
+
};
|
|
81
|
+
};
|
|
82
|
+
export type TelnyxOutboundMessage = TelnyxOutboundMediaMessage | TelnyxOutboundClearMessage | TelnyxOutboundMarkMessage;
|
|
83
|
+
export type TelnyxMediaStreamSocket = {
|
|
84
|
+
close: (code?: number, reason?: string) => void | Promise<void>;
|
|
85
|
+
send: (data: string) => void | Promise<void>;
|
|
86
|
+
};
|
|
87
|
+
export type TelnyxMediaStreamBridgeOptions<TContext = unknown, TSession extends VoiceSessionRecord = VoiceSessionRecord, TResult = unknown> = Omit<TwilioMediaStreamBridgeOptions<TContext, TSession, TResult>, 'onVoiceMessage'> & {
|
|
88
|
+
onVoiceMessage?: (input: {
|
|
89
|
+
callControlId?: string;
|
|
90
|
+
message: VoiceServerMessage<TResult>;
|
|
91
|
+
sessionId: string;
|
|
92
|
+
streamId?: string;
|
|
93
|
+
}) => Promise<void> | void;
|
|
94
|
+
};
|
|
95
|
+
export type TelnyxMediaStreamBridge = {
|
|
96
|
+
close: (reason?: string) => Promise<void>;
|
|
97
|
+
getSessionId: () => string | null;
|
|
98
|
+
getStreamId: () => string | null;
|
|
99
|
+
handleMessage: (raw: string | TelnyxInboundMessage) => Promise<void>;
|
|
100
|
+
};
|
|
5
101
|
export type TelnyxVoiceResponseOptions = {
|
|
6
102
|
bidirectionalCodec?: 'AMR-WB' | 'G722' | 'OPUS' | 'PCMA' | 'PCMU';
|
|
7
103
|
bidirectionalMode?: 'mp3' | 'rtp';
|
|
@@ -38,6 +134,7 @@ export type TelnyxVoiceSmokeOptions = {
|
|
|
38
134
|
title?: string;
|
|
39
135
|
};
|
|
40
136
|
export type TelnyxVoiceRoutesOptions<TContext = unknown, TSession extends VoiceSessionRecord = VoiceSessionRecord, TResult = unknown> = {
|
|
137
|
+
bridge?: TelnyxMediaStreamBridgeOptions<TContext, TSession, TResult>;
|
|
41
138
|
context?: TContext;
|
|
42
139
|
name?: string;
|
|
43
140
|
outcomePolicy?: VoiceTelephonyOutcomePolicy;
|
|
@@ -61,6 +158,7 @@ export type TelnyxVoiceRoutesOptions<TContext = unknown, TSession extends VoiceS
|
|
|
61
158
|
};
|
|
62
159
|
};
|
|
63
160
|
export declare const createTelnyxVoiceResponse: (options: TelnyxVoiceResponseOptions) => string;
|
|
161
|
+
export declare const createTelnyxMediaStreamBridge: <TContext = unknown, TSession extends VoiceSessionRecord = VoiceSessionRecord, TResult = unknown>(socket: TelnyxMediaStreamSocket, options: TelnyxMediaStreamBridgeOptions<TContext, TSession, TResult>) => TelnyxMediaStreamBridge;
|
|
64
162
|
export declare const verifyVoiceTelnyxWebhookSignature: (input: {
|
|
65
163
|
body: string;
|
|
66
164
|
headers: Headers;
|
|
@@ -106,6 +204,16 @@ export declare const createTelnyxVoiceRoutes: <TContext = unknown, TSession exte
|
|
|
106
204
|
};
|
|
107
205
|
};
|
|
108
206
|
};
|
|
207
|
+
} & {
|
|
208
|
+
[x: string]: {
|
|
209
|
+
subscribe: {
|
|
210
|
+
body: unknown;
|
|
211
|
+
params: {};
|
|
212
|
+
query: unknown;
|
|
213
|
+
headers: unknown;
|
|
214
|
+
response: {};
|
|
215
|
+
};
|
|
216
|
+
};
|
|
109
217
|
} & {
|
|
110
218
|
[x: string]: {
|
|
111
219
|
post: {
|
package/dist/testing/index.js
CHANGED