FreeSWITCH 1.2
上QQ阅读APP看书,第一时间看更新

Complex applications made simple

FreeSWITCH removes much of the complexity of the more advanced applications. Let's look at two examples of a more complex application.

Voicemail

The first application we will discuss is the voicemail application. The general purpose of this application is probably pretty easy to deduce. It provides voicemail service. This application is useful to add right after the bridge application as a second option, in case the call was not completed. We can do this with a careful combination of application choices and one of those fancy special variables that we were discussing earlier. Let's look at a new version of our last extension that also allows us to leave a voicemail:

<extension name="example 4">
  <condition field="destination_number" expression="^2000$">
    <action application="set"
            data="hangup_after_bridge=true"/>
    <action application="bridge" data="user/2000"/>
    <action application="voicemail"
            data="default ${domain} 2000"/>
  </condition>
</extension>

Here we see two uses of channel variables. First we set hangup_after_bridge=true telling the system to just hang up once we have had at least one successfully bridged call to another phone and to disregard the rest of the instructions. We are also using the domain variable as seen in brackets prefixed with a dollar sign, ${domain}. This is a special variable that defaults to the auto-configured domain name, which all the phones are using from the configuration.

In this example, we check if someone is dialing 2000. We then try to bridge the call to the phone registered to extension 2000. If the call fails or if there is no answer, we will continue to the next instruction, which is to execute the voicemail application. We provide the information the application needs to know and which extension the voicemail is for, so it knows how to handle the situation. Next, the voicemail application plays the pre-recorded greeting or generates one for you using the Say module's interface we briefly discussed earlier. It strings together sound files to make a voice say something like, ''The person at extension 2 0 0 0 is not available, please leave a message.'' Next, mod_voicemail prompts you to record a message, and now is your chance to leave your mark in that person's inbox by leaving a voice message. As an additional feature, if you are not satisfied with your recording, you can repeat it as many times as you wish. Once you finally commit, a FreeSWITCH MESSAGE_WAITING event is fired into the core event system, which is picked up by mod_sofia by way of an event consumer, where the event information is translated into SIP—in this case a SIP NOTIFY message that lets the SIP phone know that there is a message waiting. If everything goes as planned, the phone registered on extension 2000 will illuminate its message-waiting indicator light!

Again in this example, not only have we seen how to play a greeting, record a message, and send it to a user, we have also uncovered another unsung hero of the FreeSWITCH core—the event system. The FreeSWITCH event system is not a module interface like the other examples, it is a core engine that you can use to bind to named events and react accordingly when an event is received. In other words, throughout the FreeSWITCH core, there are events that are sent and received. Modules can bind to (that is listen for) various events. They can also fire events into the event engine; other modules can listen for those events. As we discussed, the Sofia SIP module binds or subscribes to the event designated for MESSAGE_WAITING information. This allows our mod_voicemail module to interact with mod_sofia without either system having any knowledge about the other's existence. The event is fired by mod_voicemail, received by mod_sofia, and translated into the proper SIP message—all seemingly magical, courtesy of the event system.

There are several challenges with such a complex interactive system when considering all of the possible languages it may need to support, as well as what files to play for the automated messages and how they are strung together. The Say module supplies a nice way to string files together, but it is limited to something specific like spelling a word, counting something, or saying a certain date. The way we overcome this is by defining a more complex layer on top of the Say module called Phrase Macros. Phrase Macros are a collection of XML expressions that pull out a list of arguments by matching a regular expression and executing a string of commands. This is very similar to how the XML Dialplan works, only custom-tailored for interactive voice response (IVR) scenarios. For example, when mod_voicemail asks you to record your message, rather than coding in the string of files to make it say what you want, the code just calls a Phrase Macro called voicemail_record_message. This arbitrary string is shared between mod_voicemail and the Phrase Macro section in the configuration allowing us, the users, to edit the file without doing any fancy programming.

<macro name="voicemail_record_message">
  <input pattern="^(.*)$">
    <match>
      <action function= "play-file" 
        data="voicemail/vm-record_message.wav"/>
    </match>
  </input>
</macro>

When mod_voicemail executes the voicemail_record_message macro, it first matches the pattern, which, in this case, is just to match everything, as this particular macro has no input. If the macro did have input, the pattern matching could be used to perform different actions based on different input. Once a match is found, the match tag is parsed in the XML for action tags just like in our Dialplan example. This macro just plays the file vm-record_message.wav, but more complicated macros, like the ones for verifying your recording or telling you how many messages you have in your inbox, may use combinations of various Say modules and play various audio files. Phrase Macros are discussed in detail in Chapter 6, Using XML IVRs and Phrase Macros and used extensively in Chapter 7, Dialplan Scripting with Lua.

Once again, we have cooperation among the phrase system, the audio file, and the Say modules loaded by the core being joined together to enable powerful functionality. The Say modules are written specifically for a particular language or voice within a language. We can programmatically request to say a particular time and have it translated into the proper Say module based on input variables. The Phrase Macro system is a great way to put bigger variable concepts into your code, which can be easily tweaked later by everyday users. For example, if we wanted to make a small IVR that asks us to dial a four-digit number and then just read it back and hang up, we could make one macro called myapp_ask_for_digits and the other called myapp_read_digits. In our code, we would execute these macros by name—the former when it is time to ask for the digits and the later to read back the digits by passing in the value we entered. Once this is in place, a less-experienced individual could implement the XML files to play the proper sounds. He or she can use the Say modules to read back the number, and it should all be working in multiple languages with no further coding necessary. Voicemail is just one example of FreeSWITCH in use as an application server. There are endless possibilities when we use FreeSWITCH to connect phone calls with computers.

Multi-party conferencing

Another popular feature of FreeSWITCH is delivered by the mod_conference conferencing module. The mod_conference module provides dynamic conference rooms that can bridge together the audio from several audio channels. This can be used to hold meetings where there are several callers who want to interact on the same call. Each new session that connects to the same conference room will join the others, and instantly be able to talk to all of the other participants at the same time. By using a Dialplan example, similar to the one we used for bridging to another phone, we can make an extension to join a conference room:

<extension name="example 4">
  <condition field="destination_number"  expression="^3000$">
 <action application="conference" data="3000@default"/>
  </condition>
</extension>

This is just as simple as bridging a call, but what is special about this extension is that many callers can call extension 3000 and join the same conference. If three people joined this conference and one of them decides to leave, the other two would still be able to continue their conversation.

The conference module also has other special features, such as the ability to play sound files or Text-To-Speech to the whole conference or even just to a single member of the conference. As you may have guessed, we are able to do this by using the TTS and sound file interfaces provided by their respective modules. Once again, the smaller pieces come together to extend the functionality without needing explicit knowledge of the other components in the system.

The conference module also uses the event system in a special way, employing what are called custom events. When it first loads, a module such as mod_conference can reserve a special event namespace called a subclass. When something interesting happens, such as when a caller joins or leaves a conference, it fires those events on the CUSTOM event channel in the core. When we are interested in receiving such events, all we have to do is subscribe to the CUSTOM event supplying an extra subclass string, which specifies the specific CUSTOM events we are interested in. In this case, it is conference::maintenance. This makes it possible to look out for important things such as when someone joins or leaves the conference, or even when they start and stop talking. Conferencing is discussed in detail in Chapter 14, Advanced Features and Further Reading.

The FreeSWITCH API (FSAPI)

Another very powerful module interface in FreeSWITCH is the FSAPI module. The principle of this type of interface is very simple—it takes a single string of text as input, which may or may not be parsed, and performs a particular action. The return value is also a string that can be of any size, from a single character up to several pages of text, depending on the function that was called. One major benefit of FSAPI functions is that a module can use them to call routines in another module without directly linking into the actual code. The command-line interface of FreeSWITCH or CLI uses FSAPI functions to pass FreeSWITCH API commands from an operating system's command prompt.

Here is a small example of how we can execute the status FSAPI command from the FreeSWITCH CLI:

What's really happening here is that when we type status and press the Enter key, the word "status" is used to look up the status FSAPI function from the module in which it is implemented. The underlying function is then called, and the core is queried for its status message. Once the status data is obtained, the output is written to a stream that comes back and prints as the result of the command.

We have already learned that a module can create and export FSAPI functions that can be executed from anywhere such as the CLI. But wait, there's more! Modules can also be written to push commands into the FSAPI interface and send the result over a specific protocol. There are two modules included in FreeSWITCH that do just that—mod_xml_rpc and mod_event_socket (discussed respectively in Chapter 9, Moving Beyond the Static XML Configuration and Chapter 10, Controlling FreeSWITCH Externally). Consider the example of mod_xml_rpc. This module implements the standardized XML-RPC protocol as a FreeSWITCH module. Clients using an XML-RPC interface can connect to FreeSWITCH and execute any FSAPI command they choose. So a remote client could execute an RPC call to status, and get a similar status message to the one we saw in the previous example. This same module also provides FreeSWITCH with a general web server, which allows FSAPI commands to be accessed with a direct URL link. For example, one could point a browser to http://example.freeswitch.box:8080/api/status to access the status command directly over the World Wide Web. By using this technique, it's possible to create FSAPI commands that work similar to a CGI, providing a dynamic web application that has direct access to FreeSWITCH internals.

As we have shown, the FSAPI interface is very versatile. Now we know it can be used to provide a CLI interface, a way for modules to call functions from each other, and a way to export WWW or XML-RPC functions. There is still one more use for FSAPI functions that we have not covered. We touched briefly on the concept of channel variables earlier, noting that we can use the expression ${myvariable} to get the value of a certain variable. FSAPI functions can also be accessed this way in the format ${myfunction()}. This notation indicates that the FSAPI command myfunction should be called, and that the expression should be replaced with the output of that function call. Therefore, we can use ${status()} anywhere when variables are expanded to gain access to the status command. For example:

<action application="set" data="my_status=${status()}"/>

The value placed in the my_status variable will be the output from the status command.

The drawback to all the versatility provided by a single module interface is that, in order to achieve all of this, we have to loosely type the functionality. This means that there are several cases where a single FSAPI command could easily be accessed using all of the ways we have discussed. In addition, there are also some other specific functions that are specifically designed for a particular access method. For instance, if we made an FSAPI command that produced HTML intended to be accessed with a web browser, we would not want to access it from the CLI or by referencing it as a variable. Similarly, if we made an FSAPI function that computed some kind of value based on call details, which was designed to be used from the Dialplan, it would not be very useful at the CLI or from the Web. So, with great power comes great responsibility, and this is one case where we need to use common sense to decide when and where to use the proper FSAPI functions to get the most out of them.

The XML registry

We have now discussed many of the fundamental components of the FreeSWITCH core and how they interact with each other. We have seen how the event system can carry information across the core, and how the XML Dialplan can query the XML registry for data. This would be a good time to explain the XML registry a bit more. The XML registry is a centrally managed XML document that holds all of the critical data that FreeSWITCH needs to operate properly. The initial document is loaded from your hard drive and passed into a special pre-processor. This pre-processor can include other XML documents and other special operations, such as setting global variables, which can be resolved by the pre-processor further down in the document.

Once the entire document and all of the included files are parsed, replaced, and generated into a static XML document, this document is loaded into memory. The XML registry is divided into several sections— configuration, odbc, dialplan, directory, locations, chatplan, languages, and phrases. The core and the modules draw their configuration from the configuration section. The XML Dialplan module draws its Dialplan data from the dialplan section. The SIP authentication, user lookup, and the voicemail module read their account information from the directory section. The Phrase Macros pull their configuration from the phrases section. If we make a change to any of the documents on the disk, we can reload the changes into memory by issuing the reloadxml command from the CLI. (This is an example of using the FSAPI interface to communicate with the FreeSWITCH core.)

Language modules

One distinct type of module that does not have a direct interface to FreeSWITCH-like files and Endpoints, but still offers an immensely powerful connection to existing technology, is the Language module. Language modules embed a programming language like Lua, JavaScript, Perl, and even C# (using mod_managed) into FreeSWITCH, and transfer functionality between the core and the language's runtime. This allows things like IVR applications to be written in the embedded language, with a simple interface back to FreeSWITCH for all the heavy lifting. Language modules usually register into the core with the application interface and the FSAPI interface and are executed from the Dialplan. Language modules offer lots of opportunities and are very powerful. Using language modules, you can build powerful voice applications in a standard programming language. In some respects, you can actually control a telephone with a programming language.

The demonstration configuration

Understanding all of these concepts right off the bat is far from easy, and as the maintainers of the software, we do not expect most people to have everything just click. This is the main reason that every new layer we put on top of the core makes things simpler and easier to learn. The demonstration configuration of FreeSWITCH is the last line of defense between new users of the software and all of the crazy, complicated, and sometimes downright evil stuff better known as telephony. We try very hard to save the users from such things.

The main purpose of the demonstration configuration in FreeSWITCH is to showcase all of the hundreds of parameters there are to work with. We present them to you in a working configuration that you could actually leave untouched and play with a bit before venturing into the unknown and trying your own hand at changing some of the options. Think of FreeSWITCH as a Lego set. FreeSWITCH and all of its little parts are like a brand new bucket Lego bricks, with plenty of parts to build anything we can imagine. The demonstration configuration is like the sample spaceship that you find in the instruction booklet. It contains step-by-step instructions on exactly how to build something you know will work. After you pick up some experience, you might start modifying your Lego ship to have extra features, or maybe even rebuild the parts into a car or some other creation. The good news about FreeSWITCH is that it comes out of the box already assembled. Therefore, unlike the bucket of Lego bricks, if you get frustrated and smash it to bits, you can just re-install the defaults and you won't have to build it again from scratch. The demonstration configuration is discussed in Chapter 3, Test Driving the Example Configuration.

Once FreeSWITCH has been successfully built on your system, you simply have to launch the program without changing one line in the configuration file. You will be able to point a SIP telephone or software-based SIP softphone to the address of your computer and make a test call. If you are brave and have the ambition of connecting a traditional analog phone, you may want to get the SIP thing under your belt first. This is because it involves a little more work (including purchasing a hardware card for your computer or a magic device called an ATAanalog telephone adapter).

If you have more than one phone, you should be able to configure them to each having an individual extension in the range 1000-1019, which is the default extension number range that is pre-defined in the demonstration configuration. Once you get both phones registered, you will be able to make calls across them or have them to meet in a conference room in the 3000-3399 range. If you call an extension that is not registered or let the phone ring at another extension for too long, the voicemail application will use the phrase system to indicate that the party is not available, and ask you to record a message. If you dial 5000, you can see an example of the IVR system at work, presenting several menu choices demonstrating various other neat things FreeSWITCH can do out of the box. There are a lot of small changes and additions that can be made to the demonstration configuration while still leaving it intact.

For example, using the pre-processor directives we went over earlier, the demonstration configuration loads a list of files into the XML registry from certain places, meaning that every file in a particular folder will be combined into the final XML configuration document. The two most important points where this takes place are where the user accounts and the extensions in the Dialplan are kept. Each of the 20 extensions that are preconfigured with the defaults are stored into their own file. We could easily create a new file with a single user definition and drop it into place to add another user, and simply issue the reloadxml command at the FreeSWITCH CLI. The same idea applies to the example Dialplan. We can put a single extension into its own file and load it into place whenever we want.