cloudonix-js
Version:
A JavaScript library for building and serving Cloudonix Voice Application XML (CXML) documents with support for AI voice agents, media streaming, and advanced telephony features
439 lines (381 loc) • 12.3 kB
JavaScript
/**
* @file Examples demonstrating the Dial verb and its noun elements
* @copyright 2025 Nir Simionovich, nirs@cloudonix.io
* @license MIT
*/
'use strict';
const { CXMLBuilder, CXMLServer } = require('../');
/**
* Creates a simple Dial with a direct number
* @returns {CXMLBuilder} - CXML builder with a simple dial
*/
function createSimpleDial() {
return new CXMLBuilder()
.createResponse()
.addDial('1-805-101010')
.done();
}
/**
* Creates a Dial with trunk selection
* @returns {CXMLBuilder} - CXML builder with a dial using trunk selection
*/
function createTrunkDial() {
return new CXMLBuilder()
.createResponse()
.addDial('1-805-101010', { trunks: 'outbound-trunk' })
.done();
}
/**
* Creates a Dial with multiple Number nouns
* @returns {CXMLBuilder} - CXML builder with a dial to multiple numbers
*/
function createMultiNumberDial() {
return new CXMLBuilder()
.createResponse()
.addDial({ callerId: '1234567890', timeout: 30 })
.addNumber('1-805-101010')
.addNumber('1-805-202020')
.done();
}
/**
* Creates a Dial to a SIP endpoint
* @returns {CXMLBuilder} - CXML builder with a SIP dial
*/
function createSipDial() {
return new CXMLBuilder()
.createResponse()
.addDial()
.addSip('sip:61000@example.com', {
username: 'user',
password: 'pass',
domain: 'destination'
})
.done();
}
/**
* Creates a Dial to a conference room
* @returns {CXMLBuilder} - CXML builder with a conference dial
*/
function createConferenceDial() {
return new CXMLBuilder()
.createResponse()
.addDial()
.addConference('Room 5000', {
beep: 'onEnter',
muted: false,
startConferenceOnEnter: true,
endConferenceOnExit: false,
maxParticipants: 10
})
.done();
}
/**
* Creates a Dial with a service provider
* @returns {CXMLBuilder} - CXML builder with a service provider dial
*/
function createServiceDial() {
return new CXMLBuilder()
.createResponse()
.addDial()
.addService('Service_Provider_Phone_Number', {
provider: 'vapi',
username: 'my_username',
password: 'my_password'
})
.done();
}
/**
* Creates a Dial with custom headers
* @returns {CXMLBuilder} - CXML builder with a dial using custom headers
*/
function createHeadersDial() {
return new CXMLBuilder()
.createResponse()
.addDial({ headers: 'FullName=John+Smith&Location=office' })
.addSip('sip:1234@example.com')
.addHeader('X-AuthToken', 'abcdef')
.addHeader('X-SignedCaller', '9012345678')
.done();
}
/**
* Creates a Dial with recording
* @returns {CXMLBuilder} - CXML builder with a recording dial
*/
function createRecordingDial() {
return new CXMLBuilder()
.createResponse()
.addDial({
record: 'record-from-answer',
recordingStatusCallback: '/recording-callback',
recordingStatusCallbackMethod: 'POST',
recordingStatusCallbackEvent: 'completed',
trim: 'trim-silence'
})
.addNumber('1-805-101010')
.done();
}
/**
* Creates a Dial with action URL
* @returns {CXMLBuilder} - CXML builder with a dial having action URL
*/
function createActionDial() {
return new CXMLBuilder()
.createResponse()
.addDial({
action: '/dial-complete',
method: 'POST',
hangupOnStar: true,
timeLimit: 300
})
.addNumber('1-805-101010')
.done();
}
/**
* Creates a complex Dial with multiple features
* @returns {CXMLBuilder} - CXML builder with a feature-rich dial
*/
function createComplexDial() {
return new CXMLBuilder()
.createResponse()
.addDial({
callerId: '1234567890',
callerName: 'John Smith',
action: '/dial-complete',
method: 'POST',
timeout: 60,
hangupOn: '#',
record: 'record-from-ringing',
recordingStatusCallback: '/recording-status',
trunks: 'trunk1,trunk2'
})
.addNumber('1-805-101010')
.addHeader('X-Custom-Data', 'some-value')
.done();
}
// Run as standalone script if invoked directly
if (require.main === module) {
// If running as a script, just output the XML for all examples
console.log("Example 1: Simple Dial with a number");
console.log(createSimpleDial().build());
console.log("\n");
console.log("Example 2: Dial with trunk selection");
console.log(createTrunkDial().build());
console.log("\n");
console.log("Example 3: Dial with multiple Number nouns");
console.log(createMultiNumberDial().build());
console.log("\n");
console.log("Example 4: Dial with SIP");
console.log(createSipDial().build());
console.log("\n");
console.log("Example 5: Dial with Conference");
console.log(createConferenceDial().build());
console.log("\n");
console.log("Example 6: Dial with Service Provider");
console.log(createServiceDial().build());
console.log("\n");
console.log("Example 7: Dial with custom Headers");
console.log(createHeadersDial().build());
console.log("\n");
console.log("Example 8: Dial with recording");
console.log(createRecordingDial().build());
console.log("\n");
console.log("Example 9: Dial with action URL");
console.log(createActionDial().build());
console.log("\n");
console.log("Example 10: Complex Dial with multiple features");
console.log(createComplexDial().build());
console.log("\n");
// Run the server if the --server flag is passed
if (process.argv.includes('--server')) {
runServer();
}
} else {
// Export the functions for use in other modules
module.exports = {
createSimpleDial,
createTrunkDial,
createMultiNumberDial,
createSipDial,
createConferenceDial,
createServiceDial,
createHeadersDial,
createRecordingDial,
createActionDial,
createComplexDial
};
}
/**
* Runs a CXML server with the Dial examples
*/
function runServer() {
const PORT = process.env.PORT || 3003;
// Create a new CXML server
const server = new CXMLServer({
port: PORT,
verbose: true
});
// Example routes
server.route('/simple-dial', async (context) => {
return createSimpleDial();
});
server.route('/trunk-dial', async (context) => {
return createTrunkDial();
});
server.route('/multi-number-dial', async (context) => {
return createMultiNumberDial();
});
server.route('/sip-dial', async (context) => {
return createSipDial();
});
server.route('/conference-dial', async (context) => {
return createConferenceDial();
});
server.route('/service-dial', async (context) => {
return createServiceDial();
});
server.route('/headers-dial', async (context) => {
return createHeadersDial();
});
server.route('/recording-dial', async (context) => {
return createRecordingDial();
});
server.route('/action-dial', async (context) => {
return createActionDial();
});
server.route('/complex-dial', async (context) => {
return createComplexDial();
});
// Handle callbacks from dial actions
server.route('/dial-complete', async (context) => {
console.log('Dial completed:');
if (context.body && typeof context.body === 'object') {
console.log(JSON.stringify(context.body, null, 2));
} else {
console.log(context.body);
}
return new CXMLBuilder()
.createResponse()
.addSay("The dial has completed.", {
voice: "Google:en-US-Neural2-F"
})
.addHangup();
});
// Handle recording callbacks
server.route('/recording-callback', async (context) => {
console.log('Recording status update:');
if (context.body && typeof context.body === 'object') {
console.log(JSON.stringify(context.body, null, 2));
} else {
console.log(context.body);
}
return new CXMLBuilder().createResponse();
});
server.route('/recording-status', async (context) => {
console.log('Recording status update:');
if (context.body && typeof context.body === 'object') {
console.log(JSON.stringify(context.body, null, 2));
} else {
console.log(context.body);
}
return new CXMLBuilder().createResponse();
});
// Home page with links to all examples
server.route('/', async (context) => {
// Return a simple HTML page for browser navigation
context.setHeader('Content-Type', 'text/html; charset=utf-8');
return `
<!DOCTYPE html>
<html>
<head>
<title>Dial Verb Examples</title>
<style>
body { font-family: Arial, sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; }
h1 { color: #333; }
a { display: block; margin: 10px 0; padding: 10px; background: #f4f4f4; text-decoration: none; color: #333; border-radius: 4px; }
a:hover { background: #e0e0e0; }
pre { background: #f8f8f8; padding: 10px; border-radius: 4px; overflow-x: auto; }
.section { margin-bottom: 30px; }
.description { color: #666; margin-bottom: 5px; }
</style>
</head>
<body>
<h1>Dial Verb Examples</h1>
<p>Click the links below to see different Dial verb examples:</p>
<div class="section">
<div class="description">Basic Dial to a phone number</div>
<a href="/simple-dial">Simple Dial with a number</a>
</div>
<div class="section">
<div class="description">Dial with specific trunk selection</div>
<a href="/trunk-dial">Dial with trunk selection</a>
</div>
<div class="section">
<div class="description">Dial to multiple phone numbers in sequence</div>
<a href="/multi-number-dial">Dial with multiple Number nouns</a>
</div>
<div class="section">
<div class="description">Dial to a SIP endpoint</div>
<a href="/sip-dial">Dial with SIP</a>
</div>
<div class="section">
<div class="description">Dial to a conference room</div>
<a href="/conference-dial">Dial with Conference</a>
</div>
<div class="section">
<div class="description">Dial using a service provider</div>
<a href="/service-dial">Dial with Service Provider</a>
</div>
<div class="section">
<div class="description">Dial with custom SIP headers</div>
<a href="/headers-dial">Dial with custom Headers</a>
</div>
<div class="section">
<div class="description">Dial with call recording</div>
<a href="/recording-dial">Dial with recording</a>
</div>
<div class="section">
<div class="description">Dial with callback URL when complete</div>
<a href="/action-dial">Dial with action URL</a>
</div>
<div class="section">
<div class="description">Complex Dial with multiple features</div>
<a href="/complex-dial">Complex Dial example</a>
</div>
<h2>How It Works</h2>
<p>These examples demonstrate how to use the Dial verb in Cloudonix CXML. In a real telephony application, these would connect calls to various endpoints.</p>
<h3>Example Code:</h3>
<pre>
.addDial({
callerId: '1234567890',
callerName: 'John Smith',
action: '/dial-complete',
method: 'POST',
timeout: 60,
hangupOn: '#',
record: 'record-from-ringing'
})
.addNumber('1-805-101010')
.addHeader('X-Custom-Data', 'some-value')
.done()
</pre>
</body>
</html>
`;
});
// Start the server
async function startServer() {
try {
await server.start();
console.log(`Dial Examples server running at http://localhost:${PORT}/`);
console.log('Open the above URL in your browser to see available examples');
} catch (error) {
console.error('Failed to start server:', error);
process.exit(1);
}
}
startServer();
}
// For running with --server flag from command line
if (process.argv.includes('--server')) {
runServer();
}