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
367 lines (315 loc) • 11.2 kB
JavaScript
/**
* @file Examples demonstrating the Say verb functionality
* @copyright 2025 Nir Simionovich, nirs@cloudonix.io
* @license MIT
*/
'use strict';
const { CXMLBuilder, CXMLServer } = require('../');
/**
* Creates a basic Say element
* @returns {CXMLBuilder} - CXML builder with a basic say
*/
function createBasicSay() {
return new CXMLBuilder()
.createResponse()
.addSay('Hello, welcome to our telephony system.');
}
/**
* Creates a Say with voice and language options
* @returns {CXMLBuilder} - CXML builder with voice options
*/
function createVoiceSay() {
return new CXMLBuilder()
.createResponse()
.addSay('Hello, welcome to our telephony system.', {
voice: 'Google:en-US-Neural2-M',
language: 'en-US'
});
}
/**
* Creates a Say with early media (no answer)
* @returns {CXMLBuilder} - CXML builder with early media
*/
function createEarlyMediaSay() {
return new CXMLBuilder()
.createResponse()
.addSay('You have reached our after-hours voicemail. Please call back during business hours.', {
answer: false
});
}
/**
* Creates a Say with looping
* @returns {CXMLBuilder} - CXML builder with looped say
*/
function createLoopingSay() {
return new CXMLBuilder()
.createResponse()
.addSay('This is an important announcement. Please listen carefully.', {
loop: 3 // Repeat 3 times
});
}
/**
* Creates a Say with status callback
* @returns {CXMLBuilder} - CXML builder with status callback
*/
function createCallbackSay() {
return new CXMLBuilder()
.createResponse()
.addSay('Thank you for calling. Your call is very important to us.', {
statusCallback: '/say-status',
statusCallbackMethod: 'POST'
});
}
/**
* Creates a Say with premium voice
* @returns {CXMLBuilder} - CXML builder with premium voice
*/
function createPremiumVoiceSay() {
return new CXMLBuilder()
.createResponse()
.addSay('This message is being read with a premium voice.', {
voice: 'Google:en-US-Journey-F',
language: 'en-US'
});
}
/**
* Creates a Say with SSML content
* @returns {CXMLBuilder} - CXML builder with SSML content
*/
function createSSMLSay() {
return new CXMLBuilder()
.createResponse()
.addSay('Here is an <say-as interpret-as="characters">SSML</say-as> example. You can pause <break time="3s"/> the text or emphasize <emphasis level="strong">important words</emphasis>.');
}
/**
* Creates a Say with multiple options
* @returns {CXMLBuilder} - CXML builder with multiple options
*/
function createComplexSay() {
return new CXMLBuilder()
.createResponse()
.addSay('This is a fully configured text-to-speech example.', {
answer: true,
voice: 'Google:en-US-Neural2-M',
language: 'en-US',
loop: 2,
statusCallback: '/say-complete',
statusCallbackMethod: 'POST'
});
}
// 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: Basic Say - Simple text to speech");
console.log(createBasicSay().build());
console.log("\n");
console.log("Example 2: Say with voice and language options");
console.log(createVoiceSay().build());
console.log("\n");
console.log("Example 3: Say with early media (no answer)");
console.log(createEarlyMediaSay().build());
console.log("\n");
console.log("Example 4: Say with looping");
console.log(createLoopingSay().build());
console.log("\n");
console.log("Example 5: Say with status callback");
console.log(createCallbackSay().build());
console.log("\n");
console.log("Example 6: Say with premium voice");
console.log(createPremiumVoiceSay().build());
console.log("\n");
console.log("Example 7: Say with SSML content");
console.log(createSSMLSay().build());
console.log("\n");
console.log("Example 8: Say with multiple options");
console.log(createComplexSay().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 = {
createBasicSay,
createVoiceSay,
createEarlyMediaSay,
createLoopingSay,
createCallbackSay,
createPremiumVoiceSay,
createSSMLSay,
createComplexSay
};
}
/**
* Runs a CXML server with the Say examples
*/
function runServer() {
const PORT = process.env.PORT || 3005;
// Create a new CXML server
const server = new CXMLServer({
port: PORT,
verbose: true
});
// Example routes
server.route('/basic-say', async (context) => {
return createBasicSay();
});
server.route('/voice-say', async (context) => {
return createVoiceSay();
});
server.route('/early-media-say', async (context) => {
return createEarlyMediaSay();
});
server.route('/looping-say', async (context) => {
return createLoopingSay();
});
server.route('/callback-say', async (context) => {
return createCallbackSay();
});
server.route('/premium-voice-say', async (context) => {
return createPremiumVoiceSay();
});
server.route('/ssml-say', async (context) => {
return createSSMLSay();
});
server.route('/complex-say', async (context) => {
return createComplexSay();
});
// Handle say callbacks
server.route('/say-status', async (context) => {
console.log('Say 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('/say-complete', async (context) => {
console.log('Say completion notification:');
if (context.body && typeof context.body === 'object') {
console.log(JSON.stringify(context.body, null, 2));
} else {
console.log(context.body);
}
return new CXMLBuilder().createResponse();
});
// Sequential Say examples - multiple Say verbs in one response
server.route('/sequential-say', async (context) => {
return new CXMLBuilder()
.createResponse()
.addSay("This is the first sentence.", { voice: "Google:en-US-Neural2-F" })
.addPause(1)
.addSay("This is the second sentence with a different voice.", { voice: "Google:en-US-Neural2-M" })
.addPause(1)
.addSay("And here is the conclusion.", { voice: "Google:en-US-Neural2-F" });
});
// Conversation examples
server.route('/conversation', async (context) => {
return new CXMLBuilder()
.createResponse()
.addSay("Hello! I'm the first speaker.", { voice: "Google:en-US-Neural2-F" })
.addPause(1)
.addSay("And I'm the second speaker, responding to the first.", { voice: "Google:en-US-Neural2-M" })
.addPause(1)
.addSay("It's nice to meet you!", { voice: "Google:en-US-Neural2-F" })
.addPause(1)
.addSay("Likewise! This demonstrates a conversation flow.", { voice: "Google:en-US-Neural2-M" });
});
// 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>Say 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>Say Verb Examples</h1>
<p>Click the links below to see different Say verb examples:</p>
<div class="section">
<div class="description">Basic text-to-speech with default settings</div>
<a href="/basic-say">Basic Say</a>
</div>
<div class="section">
<div class="description">Say with specific voice and language</div>
<a href="/voice-say">Say with voice options</a>
</div>
<div class="section">
<div class="description">Say that plays without answering the call (early media)</div>
<a href="/early-media-say">Say with early media</a>
</div>
<div class="section">
<div class="description">Say that repeats the message multiple times</div>
<a href="/looping-say">Say with looping</a>
</div>
<div class="section">
<div class="description">Say that sends status updates</div>
<a href="/callback-say">Say with status callback</a>
</div>
<div class="section">
<div class="description">Say using a premium AI voice</div>
<a href="/premium-voice-say">Say with premium voice</a>
</div>
<div class="section">
<div class="description">Say with Speech Synthesis Markup Language for advanced control</div>
<a href="/ssml-say">Say with SSML content</a>
</div>
<div class="section">
<div class="description">Say with multiple options combined</div>
<a href="/complex-say">Say with multiple options</a>
</div>
<div class="section">
<div class="description">Multiple Say verbs in sequence</div>
<a href="/sequential-say">Sequential Say Example</a>
</div>
<div class="section">
<div class="description">Conversation with alternating voices</div>
<a href="/conversation">Conversation Example</a>
</div>
<h2>How It Works</h2>
<p>These examples demonstrate how to use the Say verb in Cloudonix CXML. In a real telephony application, these would output spoken text using text-to-speech.</p>
<h3>Example Code:</h3>
<pre>
.addSay('This is a fully configured text-to-speech example.', {
answer: true,
voice: 'Google:en-US-Neural2-M',
language: 'en-US',
loop: 2,
statusCallback: '/say-complete',
statusCallbackMethod: 'POST'
})
</pre>
</body>
</html>
`;
});
// Start the server
async function startServer() {
try {
await server.start();
console.log(`Say 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();
}