cancel
Showing results for 
Search instead for 
Did you mean: 
cancel
17268
Views
70
Helpful
21
Comments
Gergely Szabo
VIP Alumni
VIP Alumni

This is a collection of recipes I use(d) working with UCCX scripts.

Feel free to use them - I hope you will find them useful.

1. Check credit card number validity (Luhn algorithm)

2. Java version for the ultra lazy

3. Poor, but elegant man's way of sending emails (JavaMail)

4. Poor, less elegant man's way of sending emails (Sockets)

5. Generating a random string

6. Measuring script execution time

7. Getting the prompt's length in seconds (G.711 uLaw)

8. Get enterprise variables without knowing their names

9. Store information in a properties file

1. Check credit card number validity (Luhn algorithm)

We can use the Luhn algorithm to test whether a credit card number is valid.

For example, the customer may enter the credit card number using DTMF and before actually sending it to a backend database, you can check its validity.

Please note this algorithm does not check the existence of a credit card, only the validity of its number.

I am going to introduce two variables:

ccnumber - type String, initial value "" - this holds the credit card number

ccvalid - type byte, initial value (byte)0 - this is where the algorith reports the card's validity

Insert a new Set step into your script - the return variable is ccvalid, and the value is the following block of code:

{

int sum = 0;

boolean alternate = false;

boolean isValid = false;

try {

int i = 0;

for (i = ccnumber.length() - 1; i >= 0; i--) {

  int n = Integer.parseInt(ccnumber.substring(i, i + 1));

  if (alternate) {

   n = n * 2;

   if (n > 9) {

    n = (n % 10) + 1;

   }

  }

  sum += n;

  alternate = !alternate;

}

isValid = (sum % 10 == 0);

} catch (Exception e) {

return -1;

}

return (isValid == true ? 1 : 0);

}

The value of ccvalid after this step will be:

  • 1 if the credit card number is valid;
  • 0 if the credit card number is invalid;
  • -1 if an error happened (for instance, the supplied credit card number contained letters).

uccx_luhn.PNG

↑GTT↑


2. Java version for the ultra lazy

If you have multiple UCCX installations and you want to check the Java version without opening the documentation (for example, you do not have internet connection or you are just being ultra lazy), you can use the following snippet to get the Java version - or, more precisely, the Java Development Kit (JDK) version for your UCCX.

Just for the demonstration, I am going to use a new variable: javaVersion, type String, initial value "".

Then a Set step, with the variable javaVersion, and the value will be this piece of code:

System.getProperty("java.version")

Insert this step into a new script, and then just press F10 (= Step Over) until the cursor goes past this step and watch the value of the javaVersion variable. It should be something like "1.6.0_17" - meaning it's JDK 6.

The step:

uccx_java_version_1.PNG

And the value of the javaVersion variable (this is UCCX 8.0):

uccx_java_version_2.PNG

↑GTT↑


3. Poor, but elegant man's way of sending emails (JavaMail)

Of course, it's easier to use the Create email step (provided you've got the correct UCCX licenses, at this moment it's Premium and IP IVR only), but Java gives you more control and more possibilities:

  • you can send emails to multiple addresses, and use Cc: and Bcc: fields;
  • if you are not afraid of some additional Java coding you can even do rich text and HTML emails, with all kinds of attachments.

You can read a nice introduction about JavaMail on Wikipedia.

This example script sends a plain text email with a body containing non-ANSI characters (a sentence in Hungarian) to multiple addresses, with a blind carbon copy (Bcc:). Authentication is not required.

I am going to use these variables:

  • smtpHost - type final String, value "192.168.1.18" - the SMTP server (marked final because I am not going to change it in the script);
  • smtpPort - type final int, value 25 - the SMTP port;
  • fromEmailAddress - type String, value "" - this is the email address that appears in the From: field, e.g. somebody@someplace.net;
  • fromEmailPersonal - type String, value "" - this is the so-called personal part of the From: field, e.g. "Somebody You Love";
  • recipientEmailAddress - type String, value "" - this is the email address I am going to send this email to (appears as To: );
  • bccAddress - type final String, value "email@address.here" - this email address is going to get a blind carbon copy of this email;
  • emailBody - type String, value "" -  the text I am going to send as the email body;
  • emailSubject - type String, value "" - the Subject: field;
  • emailSent - type boolean, value false - just to see whether the email has been processed correctly.

uccx_javamail_vars.PNG

Please note, some variables are declared final, meaning you won't be able/don't need to change in within the script. It's a matter of choice.

And the steps

1. Setting the values of fromEmailAddress, fromEmailPersonal, recipientEmailAddress, and the emailSubject:

ucc_javamail_1.PNG

2. Then constructing the email body, and assign it to the emailBody variable. I am going to use a block of code here, concatenating different strings (with StringBuffer, one may also use StringBuilder):

{

StringBuffer myBuf = new StringBuffer();

String newLine = System.getProperty("line.separator");

myBuf.append("Dear business partner," +  newLine);

myBuf.append("I hope your enjoyed our talk last night about project 'Árvíztűrő tükörfúrógép'. " +  newLine +  newLine);

myBuf.append("Sincerely," +  newLine );

myBuf.append("Me.");

return myBuf;

}

Notice the usage of the "line.separator" system property - that's what makes your script universal.

ucc_javamail_2.PNG

3. And the most important part: constructing the email, and handing it over to the SMTP server:

{

// setting the context properties:

java.util.Properties props = new java.util.Properties();

props.put("mail.smtp.host", smtpHost);

props.put("mail.smtp.port", smtpPort);

// creating a new mail session:

javax.mail.Session session = javax.mail.Session.getInstance(props);

try {

// new MIME message, version 1.0:

javax.mail.Message message = new javax.mail.internet.MimeMessage(session);

message.setHeader("MIME-Version" , "1.0" );

// From: address, including the Personal part:

message.setFrom(new javax.mail.internet.InternetAddress(fromEmailAddress, fromEmailPersonal ));

// then the recipient (To:) of course, you can use multiple recipients with the same command

// read the JavaMail API

message.setRecipients(javax.mail.Message.RecipientType.TO, javax.mail.internet.InternetAddress.parse(  recipientEmailAddress ));

// adding the Bcc: address:

message.setRecipients(javax.mail.Message.RecipientType.BCC, javax.mail.internet.InternetAddress.parse( bccAddress ));

// setting the Subject:

message.setSubject( emailSubject );

// setting the email body:

message.setText(emailBody);

// finally, sending the email

javax.mail.Transport.send(message);

} catch (Exception e) {

// something went wrong, bail out, return false:

return false;

}

// no exceptions raised, return true:

return true;

}

This step assigns the value of the emailSent boolean variable:

  • true: email has been processed, no exceptions,
  • false: exception(s) have been caught, there must have been a problem while constructing/sending the email.

Also a screenshot, without comments:

ucc_javamail_3.PNG

This is what I got (my address being the recipient):

ucc_javamail_4.PNG

And, of course the person on the Bcc: got the same email too.

↑GTT↑


4. Poor, less elegant man's way of sending emails (Sockets)

In case you don't want to / can't use JavaMail, you can still use sockets to send a simple email message.

I am going to reuse the same variables as in the previous recipe (for the various addresses, except for the Bcc one, and the email body). But this time, for sending the email this code is used:

{

try {

//creating a new socket is easy:

java.net.Socket socket = new java.net.Socket(smtpHost, smtpPort);

// this might be unnecessary, but it's always good to set the buffer size:

socket.setReceiveBufferSize(60000);

// again, it's very important to be universal and not to use platform specific line separator strings like \r\n (Windows) or \n (Unix):

String separator = System.getProperty("line.separator");

// a new Reader and Writer represents the communications' input and output channel:

java.io.BufferedReader reader = new java.io.BufferedReader(new java.io.InputStreamReader(socket.getInputStream()));

java.io.PrintWriter writer = new java.io.PrintWriter(new java.io.OutputStreamWriter(socket.getOutputStream()));

// this is required by some SMTP servers (Postfix in my case): reading the first line before anything is sent. Your SMTP server might not require this:

String smtpLine = "";

smtpLine = reader.readLine();

// say EHLO:

writer.write("EHLO localhost" + separator);

writer.flush();

// mail from:

writer.write("MAIL FROM:<" + fromEmailAddress + ">" + separator);

writer.flush();

// rcpt to:

writer.write("RCPT TO:<" + recipientEmailAddress + ">"+ separator);

writer.flush();

// and finally, the email itself:

writer.write("DATA" + separator);

writer.flush();

writer.write("From:" + fromEmailPersonal + " <" + fromEmailAddress + ">" + separator);

writer.flush();

writer.write("Subject:" + emailSubject + separator);

writer.flush();

writer.write(emailBody + separator);

// this is where we send it:

writer.write(separator + "." + separator);

writer.flush();

// closing the door:

writer.write("QUIT" + separator);

} catch (Exception e) {

// something went wrong, bail out, return false:

return false;

}

return true;

}

Obviously, since we are not reading anything, we don't now what the SMTP server tells us (for instance, it might not be allowed to relay for that particular recipient). You can use reader.readLine() to read from the InputStream, just to enhance the above very simplistic script.

ucc_javamail_socket.PNG

And, this is the email I got this time (me being the recipient):

ucc_javamail_socket_.PNG

↑GTT↑


5. Generating random strings

Why do we need them?

  • To create unique file names (for instance, when using a quick and dirty voice mail using the Recording + Email steps).
  • A custom database identifier (alright, there are better ways, using the database system itself).

There are actually two more ways. Two examples:


Using UUID

Starting JDK 5, we can use the UUID class, which produces an "immutable universally unique identifier". Like this (provided you've got a String type variable named uuidRandomString):

Set uuidRandomString = java.util.UUID.randomUUID()

This will generate a string like "cffd0927-6d16-40e0-a2b8-27441e53cd3a"

Using the actual DateTime and Random Number

This should work with JDK < 5 as well, even though there's a tiny little chance the result won't be unique. It's basically getting the actual date and time down to milliseconds, formatting it, and then attaching a random number from the 0..9999 range (this example uses a variable named simpleRandomString, of type String):

Set simpleRandomString =

{

String DATE_FORMAT="yyyyMMdd_HHmmss-SSS";

java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat(DATE_FORMAT);

String sNow = sdf.format(new Date());

java.util.Random random = new java.util.Random();

return sNow + "X" + random.nextInt(9999).toString();

}

And it should generate a random string like "20120502_212917-340X3844".

uccx_randstrings.PNG

↑GTT↑


6. Measuring script execution time

If you ever wanted to know how long does it take to execute certain steps / (almost) the whole script, then this might be a good way to measure it.

For instance, if there's an expensive operation (like database reads and writes) you can see the amount of time it takes - down to nanoseconds.

For this demonstration, I am going to use three variables, all of type long:

  • startNanos - marks the start of the measured interval,
  • endNanos - marks the end of the interval,
  • executionTimeNanos - the interval's length, in nanoseconds.

We are going to use the System.nanoTime() method (available since JDK 5) which gives more precise information than System.currentTimeMillis().

Set startNanos = System.nanoTime()

/* series of expensive operations */

Set endNanos = System.nanoTime()

Set executionTimeNanos = endNanos - startNanos

The value of executionTimeNanos is roughly the amount of nanoseconds that took the "series of expensive operations" to run.

For example, executionTimeNanos = 5291900000L means 5.292 seconds.

uccx_nano_end.PNG

↑GTT↑


7. Getting the prompt's length in seconds (G.711 uLaw)

This comes useful if

  • your script uses the Recording step,
  • or there's a reference to an external prompt in your script (for instance by referencing an URL or a File Document which is actually a Wave file)

.. and you're interested in the length of the prompt.

Why?

It's important to get the prompt's (audible) length before certain steps, for example, you might want to refuse extremely long prompts (like 5 minutes) or you might want to decide to save wave files created by the Recording step elsewhere (e.g. short prompts to a database, large prompts to a file system).

I am going to use two variables:

myPrompt - type Prompt, initial value P[] which is the default - this is the Prompt you just downloaded or recorded;

promptLength - type float, initial value 0.0F (default) - this will be the prompt's length.

For this demostration's sake, pretend, the myPrompt has already got its value - I am going to use a prompt that is in the prompt repository, named busy.wav (it's just a regular G.711 uLaw wave file)

For the calculation, use a Set step, where the return variable will be promptLength and the value will be this piece of code:

{

try {

Document doc = (Document) myPrompt;

javax.sound.sampled.AudioInputStream ais = javax.sound.sampled.AudioSystem.getAudioInputStream(doc.getInputStream());

int avail = ais.available();

return  avail / 8000f;

} catch (Exception e) {

return -1;

}

}

The value of promptLength will be -1 if an error occured, in other cases it will be the prompt's length in seconds, e.g. 12.928F meaning 12 seconds and 928 milliseconds.

It works with G.711 uLaw only - javax.sound.sampled.* cannot work with G.729 audio streams (I guess that's because G.729 is covered by an unfriendly patent).

This script also assumes you feed it with a prompt UCCX can work with, so it's 8000 frames per second and 1 bytes per frame, and of course 1 channel (mono).

Example:

uccx_pl_1.PNG

And the prompt's length is roughly 13 seconds:

uccx_pl_2.PNG

↑GTT↑


8. Get enterprise variables without knowing their names

This works in a UCCE environment, where UCCX is actually IP IVR.

You can get the enterprise variables (PeripheralVariable 1..10 and Expanded Call Context aka ECC variables) - those sent and expected by the ICM - easily, without knowing their names.

For instance, you may want to check if all expected ECC variables arrived from ICM. Or, you just want to iterate through all variables, looking for a needle in a haystack, for a specific value within the set of values of all PeripheralVariables and ECC's.

In this example, I already set up everything necessary on the ICM side, there is an ICM script, which sets a couple of variables and then sends the call to our UCCX script which is discussed here.

We will use four variables:

contact - type Contact - initial value null - this is where we store the contact object,

expVariables - type java.util.HashMap - initial value null - this HashMap will store our ECC variables,

s - type String - initial value "" - just for debugging purposes,

variables - type String[] - initial value null - this String array will store our "regular" PeripheralVariables plus other useful stuff.

First, we need to get the contact object. We use the Get Trigger Info step for it:

Next, let's get the "regular" variables, including PeripheralVariable 1..10 and some other useful information. We use a String[] (String array) named variables and a Set step for this:

Set variables =

{

com.cisco.call.CallContact callContact = (com.cisco.call.CallContact) contact;

com.cisco.call.CallDataContextMap contextMap = com.cisco.call.CallDataContextMap.instance();

com.cisco.call.CallDataContext callDataContext = contextMap.get(callContact.getImplId().toString());

return callDataContext.getVariables();

}

Now, if we run reactive debugging, we see the variable variables populated with the following values:

new String[] {"jstest.aef", "config param", "072512345678", "55", "", "", "", "", "", "", "peripheralVariable7", "", "", "", null, null}

Remember, it's an array, so it's an ordered list of String values. What are those values?

index value
0 The name of the external script triggered by ICM - it's basically the handle we use in ICM to trigger our UCCX script.
1 This is the Configuration Parameter setting in the ICM Configuration Manager, Network VRU Script List.
2 ANI.
3 CED.
4 PeripheralVariable1
5 PeripheralVariable2
6 PeripheralVariable3
7 PeripheralVariable4
8 PeripheralVariable5
9 PeripheralVariable6
10 PeripheralVariable7
11 PeripheralVariable8
12 PeripheralVariable9
13 PeripheralVariable10
14 ??
15 ??

As we can see, variables[10] - PeripheralVariable7 - has a value of "peripheralVariable7". This is what I have set in my ICM script:

The next step will be used to harvest the ECC variables (unfortunately, this works only for simple ECC's, not for arrays). We will have the expVariables variable - of type java.util.HashMap - to store our ECC variables.

Set expVariables =

{

com.cisco.call.CallContact callContact = (com.cisco.call.CallContact) contact;

com.cisco.call.CallDataContextMap contextMap = com.cisco.call.CallDataContextMap.instance();

com.cisco.call.CallDataContext callDataContext = contextMap.get(callContact.getImplId().toString());

return  callDataContext.getExpVariables();

}

Again, if we do reactive debugging, we can see our expVariables as:

java.util.HashMap?{user_prompt=rb_friday.wav, user_mail=pig@sty.com}

It's perfect, because this is what we set in the ICM script:

If you are new to Java, a HashMap is a list of key-value pairs. In this example, we have to pairs:

key: user_prompt, value: rb_friday.wav

key: user_mail: value: pig@sty.com

Notice the difference between the ICM notation and the UCCX on: Call.user_prompt becomes simply user_prompt, Call.user_mail becomes user_mail.

You may want to ask: what if we use the Set Enterprise Call Info Step before running the above code? The CallDataContext is updated immediately, so the above code will reflect the change. In fact, I set the value of CED - aka Call.CallerEnteredDigits - in UCCX, not in ICM, using a Set Enterprise Call Info step. And it appeared in the variables String[].

Hint: if you are new to Java, this is an example of iterating over the keys and values of a HashMap. The following piece of code simply creates a String like this:

"user_prompt: rb_friday.wav; user_mail: pig@sty.com; "

We use the following code in a Set step, resulting the value of the variable named s, type String:

Set s =

{

StringBuffer buffer = new StringBuffer(50);

java.util.Iterator entries = expVariables.entrySet().iterator();

while (entries.hasNext()) {

    java.util.Map.Entry entry = (java.util.Map.Entry) entries.next();

    String key = (String) entry.getKey();

    Object value = (Object) entry.getValue();

    buffer.append(key + ": " + value + "; ");

}

return buffer.toString();

}

If you happen to know how to get the ECC arrays, please let me know.


↑GTT↑

9. Store information in a properties file

You don't need XML if you wan to store a small set of key-pair values. The Properties Java class gives you simple tools for storing and loading information from a human-readable text file. For instance:

properties-contents.png

It cannot be simpler. We have a comment ("This is just a comment in the properties file."), a timestamp ("Mon Jun 03 17:48:26 CEST 2013") when this file was created, and two key-pair values: "closed" set to "yes", and "reason" set to "weather".

With a few lines of Java code, we can easily create such files and also read their contents.

Notice: this is a UCCX 7.0SR5 I am demonstrating on, so I have the possibility of writing the file directly onto the local disk, with the"Write Document" step. With 8.0 and higher, this is not possible, but you may use steps like "Upload Document" or take a look at the documents about writing files on a Windows share or using SFTP or FTP.

Creating and modifying a properties file:

I am going to use two variables:

filename - type String - initial value "c:/temp/cc-config.properties"

s - type String - initial value: ""

Add a Set step, variable s, and for the value, use this code block:

{

java.util.Properties props = new java.util.Properties();

props.setProperty("closed", "yes");

props.setProperty("reason", "weather");

String comments = "This is just a comment in the properties file.";

java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();

props.store(baos,comments);

return baos.toString();

}

Then, add a Write Document, or an Upload Document step, with the Document set to (Document) s, and the Filename (in the case of the Write Document step): fileName.

What happened here?

In the Set step, I created a new Properties object, named props. Set the property of "closed" to "yes", and also set the property of "reason" to "weather".

I also created a new String, named comments, with the following value: "This is just a comment in the properties file.".

Then I created a buffer, of type ByteArrayOutputStream and sent my props object there. This serialized it.

Finally, I wrote this serialized object (it's actually a String) to a file.

The result is a file named cc-config.properties, in c:\temp, with the following contents:

#This is just a comment in the properties file.

#Mon Jun 03 18:12:34 CEST 2013

closed=yes

reason=weather

Here's a screenshot of this "setter" script:

props-setter.png

Reading information from a properties file:

Added the following variables to a new script:

closed - type String - initial value: ""

reason - type String - initial value: ""

exitCode - type int - initial value: 0

doc - type Document - initial value DOC[]

fileName - type String - initial value: "c:/temp/cc-config.properties"

Then, added a Set step - it just loads the file into the memory. Since it is a file document, I use the Set doc = FILE[fileName] construct. If it were a document in the Document Repository of the UCCX, I would have used something like this: Set doc = DOC[documentName]

One simple Set step would read the properties from the file, assigning the values to the local variables (closed, reason), and alternatively, inform us whether the read operation was successful or not: if the exitCode value is -1, something must have happened (e.g. file does not exist or not correctly formatted), otherwise, if 0, everything went just alright.

Set exitCode = {

java.util.Properties props = new java.util.Properties();

try {

props.load(doc.getInputStream());

closed = props.getProperty("closed");

reason = props.getProperty("reason");

} catch (Exception e) {

return -1;

}

return 0;

}

What happens here: first I create a new, empty Properties object, named props. Then take the InputStream from the doc variable (it's like opening a bottle), and load the properties, using the load method. Next, I get the "closed" property and assign its value to the local closed variable. Same with the property "reason". Finally, I return either -1 if an exception (~ error) occured, or 0 if it did not. As the following screenshot demonstrates it, the values of the variables are set, according to the properties file:

props-varsset.png

Also, a screenshot of the "getter" script:

props-getter.png


↑GTT↑

Comments
kmitty
Community Member

It's not working on UCCX 10.6 too. The problem is RecipientType and for now I can't resolve it or find workaround. Here is the log fragment (always same error), full log attached

=== cut here ===

446710: Aug 15 11:58:32.466 MSK %MADM-SCRIPT_MGR-3-UNABLE_LOAD_SCRIPT:Unable to load script: Script=PC/TestEMail.aef,Exception=java.io.InvalidClassException: javax.mail.Message$RecipientType; local class incompatible: stream classdesc serialVersionUID = -7479791750606340008, local class serialVersionUID = 8926476023043427979
446711: Aug 15 11:58:32.467 MSK %MADM-SCRIPT_MGR-3-EXCEPTION:java.io.InvalidClassException: javax.mail.Message$RecipientType; local class incompatible: stream classdesc serialVersionUID = -7479791750606340008, local class serialVersionUID = 8926476023043427979

=== cut here ===

kmitty
Community Member

Same error on UCCX 10.6 - script is perfectly running and working(!) from UCCX Editor, but failed to load into application. See my previous comment

BPaz
Level 1
Level 1

Hi Gergely,

 

Really nice Job. I found very helpful the first code for Credit Card Validation.

 

Hope that you continue making great collaborations.

ryanticer
Level 1
Level 1

Great work on all of these...very helpful.

 

A note on #7 - the javax.sound.sampled.AudioSystem.getAudioInputStream() method requires an input stream source that supports mark/reset (AKA stream navigation). Recorded prompts in UCCX, when using the .getInputStream() method, do not support mark/reset. I had to modify the code to utilize a java.io.BufferedInputStream as so:

 

{

    try {

        Document Recording = (Document) DP[5000]; // Only here to get a Document for sample

        java.io.BufferedInputStream bis = new java.io.BufferedInputStream(Recording.getInputStream());

        javax.sound.sampled.AudioInputStream ais = javax.sound.sampled.AudioSystem.getAudioInputStream(bis);

        int avail = ais.available();

        return avail / 8000f;

    } catch (Exception e) {

        return -1;

    }

}

 

EDIT: Forgot to mention, this was on UCCX 11.6(2).

 

Thanks!

Ryan

e23
Level 1
Level 1

Hello, Could you please help me.

This code is returning null in UCCX Editor but working in Eclipse, it returns an array of string. I should take anything in consideration while using this code inside UCCX? MyArray is defined as array of string from UCCX side.

javax.xml.parsers.DocumentBuilder builder = javax.xml.parsers.DocumentBuilderFactory.newInstance().newDocumentBuilder();
org.xml.sax.InputSource src=new org.xml.sax.InputSource();
src.setCharacterStream(new java.io.StringReader(s));
org.w3c.dom.Document doc = builder.parse(src);

org.w3c.dom.NodeList nodeList = doc.getElementsByTagName("Data1");int nl=nodeList.getLength();
javax.xml.xpath.XPath xpath =javax.xml.xpath.XPathFactory.newInstance().newXPath();
javax.xml.xpath.XPathExpression expr = xpath.compile("//Data1/Data2/Data3");
org.w3c.dom.NodeList List = (org.w3c.dom.NodeList) expr.evaluate(doc, javax.xml.xpath.XPathConstants.NODESET);
for (i = 0; i <=nl-1; i++) {
org.w3c.dom.Node node = List.item(i);
MyArray[i]=node.getTextContent();
}

return MyArray;

e23
Level 1
Level 1

Hello, we have a certificate installed on a soap server and we are trying to connect to it using java and uccx editor,

I would like to know if it's possible to do it using java.

When we added the certificate files to the uccx it returned handshake failed exception. 

Getting Started

Find answers to your questions by entering keywords or phrases in the Search bar above. New here? Use these resources to familiarize yourself with the community: