David Sugar
Open Source Telecom.
[email protected], http://www.gnu.org/software/bayonne
2003-01-03
GNU Bayonne is a script driven telephony application server. As such, it has it's own scripting language built by class extension from the GNU ccScript interpreter. Many people ask why GNU Bayonne has it's own scripting language rather than using an existing one. There are several reasons for this.
First, GNU ccScript is deterministic. In some scripting languages, expressions can be parsed and evaluated as arguments and mid-statement, including expressions that are long and have no fixed runtime duration. GNU Bayonne requires realtime response and non-blocking behavior; it cannot directly execute things that have undetermined execution times.
To reduce system load and support a very high number of concurrent script sessions (up to 1000), we looked for being able to execute script statements on the leading edge of a callback event rather than requiring a seperate thread to support each interpreter instance. Most script interpreters, besides being non-deterministic in their behavior, also do not support direct single stepping in that manner.
In GNU ccScript, it is also possible to effectivily isolate and seperate execution of statements that require extended duration to execute or that might block (such as I/O statements) from those that can be executed quickly from a callback handler in realtime. A thread can then be used to support execution of a statement that may otherwise block or delay realtime response. Most scripting languages do not directly support seperation of execution for "quick" and "slow" statements since they are not directly concerned with the effect of blocking operations.
Finally, we wanted to execute scripts immediately and from memory context. The reason for this is that loading a script file from disk is of course a potentially blocking operation, and is an operation that would need to be frequently done in most scripting systems. GNU ccScript forms a single interpreter image by compiling all scripts at once directly into memory. Furthermore, since we did not wish to have the server "down" to load a new script image, we can maintain more than one such core image; when new scripts are loaded, any calls currently in progress continue to use the previously active script image from memory. New calls are offered the script a new image if the server has been asked to load one. When the last of the older calls complete, then the older image is purged from memory. This permits continual server uptime even while replacing scripts that are held in memory.
Each script statement is a single input line in a script file. A script statement is composed of four parts; an event flag as appropriate, a script command statement, script command arguments, and keyword value pairs. White spaces are used to seperate each part part of the command statement, and white spaces are also used to seperate each command argument. Each script file is considered a self contained application, and may itself be broken down into named sections that are labelled and individually referenced.
Line input is not limited to 80 characters. Starting with ccScript 2.4.4, it is not limited in any manner whatsoever. However, to make it easy to edit very long lines, they can be split up by using a at the end of a line to join multiple lines together.
Script commands may be modified by a special .member to describe or activate a specific subfunction. For example, a "foo.send" command, if it existed, would be different from but still related to a plain "foo" command. Members are often used for special properties, to specify an offset value, or the size of script symbols that are being created.
Command arguments are composed either of literal strings or of references to symbols. Symbols are normally referenced by starting with the ``%'' character and can be typed in a variety of ways explained below. A group of arguments may also appear within double quotes, and these will be expanded into multiple arguments that are composed of literal string constants and the substituted values of variables that are referenced. Double quoted arguments are most often used to compose strings that are to be parsed by other subsystems (such as sql statements) or to format output for logging.
Since sometimes it is useful to refer to arguments that may appear as strings or other forms of content, 's be used to enclose a literal section. A literal section is passed as a single string argument regardless of the content it contains. Hence, while ``my value is %1'' would form two string arguments, 'my value is ', and the value of %1, the literal expression my value is %1 would return a single string argument in the form 'my value is %1' unmodified. 's may also be used to pass character codes that are normally not supported as command arguments.
A complete script defines a labeled section of a script file, the script statements the labelled section contains, and the statements found in any event handlers associated with the label. The script file itself has a default label under the name of the script, and any statements that appear before labels are used are associated with a label named by the script file itself. When event handlers are attached to labeled sections of a script, and control passes to the event handler, the script blocks all remaining events until a new script section is entered.
Labels are used to segment script files into individual script entities. These entities are all stored and referenced from a common hash table. Script labels that are the default script code for a given .scr file are stored under the name of the script itself. Within the script, local labels may be used. These local labels can be in the form ``::label'' or ``label:''.
A script file that has commands which need to branch to a label can do so by branching to the local script file name. Hence, if there is a ``test:'' entry in ``your.scr'', another script command can branch to the test: entry directly by using ::test, as in ``goto ::test'', for example.
Labels within other script files, unless made private, may also be referenced and branched to directly. If myscr.scr has a script section ``test:'', I can branch to it by refering to myscr::test. Hence, I could use ``goto myscr::test'' for example.
Script labels can be made private within a given script file using the ``.private'' directive. This means that they can only be referenced from other scripts within the same file in the ``::xxx'' form, and not externally through ``yyy::xxx'' references. By default all script labels are public. You can also use ``.public'' as a directive to convert the next script labels in a file to public after using ``.private''.
Script labels may also be exported into another script. This can be useful when making a script label private but wishing to make it available as or part of another script file. This is done with the ``.export'' directive. Exported scripts are named, compiled, behave, and are referenced as if they appeared in the file named in the .export statement rather than the .scr file they appear in.
Bayonne scripting recognizes three kinds of symbols; constant "values" (literals), %variables, and @indirection. In addition, Bayonne recognizes compile time substitutions, known as $names, which can substitute to any of the above three. A literal can be "string" literals, which are double quoted, numeric literals, such as 123, which are without quotes, and {as-is string} literals encased in {}'s. (see Sec.2).
A %variable can be defined either as having content that is alterable or that is constant. Some %variables are automatically initialized for each and every telephone call, while others may be freely created by scripts themselves. However, all variables and values stored, with the exception of globals, are automatically cleared at the end of each telephone call.
Constant variables are declared with the ``const'' keyword. Sometimes this is useful to have a non-modifiable variable when defining a value that may have an overiding default which can be asserted at an earlier point in the code. This is especially important in subroutine calls and argument passing, as will be explained later. However, most often, variables are used to store changable values.
Bayonne also recognizes symbol scope. Local symbols are created within symbol scope, resulting in unique instances within subroutines. In addition, all local symbols in symbol scope are removed when returning from a subroutine. global symbols are visible everywhere under the same instance. A ``global'' symbol uses ``.'' notion, as in %myapp.name, where a ``local'' symbol uses a simple name, as in %name, for example. Generally it is suggested that a given script should organize it's global symbols under a scriptname.xxx format to make it easier to read and understand.
A fourth class of symbol exists only at compile time, $defined symbols are substituted when the script file is compiled, and usually reduce to a simple constant, though variables can be named for the compiler. All constants are defined in the [script] section of bayonne.conf.
You can concatenate symbols and constant strings with either non-quoted whitespace or the comma operator. For example,
set %a ``a'' ``b'', ``c''
results in %a being set to ``abc''.
Finally, there is a shortcut notation to create global symbols that are associated with a single script file. If we have a script named foo.scr, and wish to create a bunch of global symbols related to ``foo'', we do not have to create %foo.xxx, %foo.yyy, etc. Instead, we can refer to these script global names simply as .xxx and .yyy. These will be expanded at compile time back to %foo.xxx and %foo.yyy. When in another script file, one can reference the full name, %foo.yyy directly, while the shortcut form can be used within the script file, or within things exported as foo.
The following variables are commonly defined:
%script.error | last error message |
%script.token | current token seperator character |
%script.home | same as %session.home |
%script.trigger | name of armed symbol that was triggered |
%return | process exit value of last libexeced TGI script |
%session.id | global call identifier |
%session.date | current date |
%session.time | current time |
%session.digits | currently collected dtmf digits in the digit buffer |
%session.count | count of number of digits collected |
%session.starttime | time of call starting |
%session.startdate | date of call starting |
%session.duration | current call duration |
%session.callerid | generic identity of calling party |
%session.calledid | generic identity of how they called us |
%session.home | initial script invoked |
%session.schedule | script that was scheduled |
%session.eventsenderid | port that sent a message with the ``send'' command |
%session.eventsendermsg | the message given to the ``send'' command |
%session.language | current language in effect (default = "english") |
%session.voice | current voice library in effect (default = "UsEngM") |
%session.joinid | last port we successfully joined with. |
%session.parent | id of call session that started current one. |
%session.pickupid | last port that picked us up. |
%session.transferid | port that is being transferred to us. |
%session.recallid | port we successfully picked up. |
%session.extension | extension number call is part of. |
%session.loginid | effective user id for preferences database |
%user.* | preference database entries for this user |
%line.* | line settings we may have in effect |
%driver.id | driver timeslot associated with call session. |
%driver.card | physical card number associated with session. |
%driver.span | T1/E1 span number associated with session. |
%driver.network | Type of port switching; "none", "soft", or "tdm" |
%pstn.dnid | did/dnis number dialed if available |
%pstn.clid | ani or caller id number if available |
%pstn.name | caller name if passed in callerid |
%pstn.redirect | if origin is a telco redirect |
%pstn.ringid | if distinctive ringing is available |
%pstn.infodigits | if telco infodigits were passed to us |
%pstn.rings | number of rings so far |
%pstn.interface | Type of pstn interface; analog, digital, etc. |
%pstn.tone | Last special pstn tone detected. |
%policy.name | name of policy for this session |
%policy.member | nth member of this policy |
%policy.* | various policy variables. |
%audio.volume | volume level in 0-100 for play and record. |
%audio.extension | default audio file extension (.au, .wav, etc) |
%audio.format | default audio format (ulaw, alaw, g721, etc) |
%audio.annotation | annotation of last played or for recorded file |
%audio.played | samples played from played file |
%audio.recorded | samples recorded to record file |
%audio.created | date played file was created |
%audio.timeout | timeout in a play wait |
%audio.trim | minimal number of samples to trim at end of file |
%audio.offset | sample offset of play or record at end of command |
%audio.buffer | driver specific buffering for computing offsets |
%server.ports | total ports on bayonne server |
%server.version | version of bayonne server |
%server.software | software identifier; "bayonne" |
%server.driver | which driver we are running |
%server.node | node id of our server |
%rpc.status | last posted rpc status result |
%rpc.loginid | rpc requestor's login identifier |
%rpc.expires | time period for rpc to complete by |
%sql.driver | name of loaded sql driver |
%sql.rows | number of rows returned in last query |
%sql.cols | number of cols returned in last query |
%sql.database | name of database connected to |
%sql.error | last sql error received |
%sql.insertid | id of last insert operation |
A special set of variables may be created by the application program under the name %global. global.xxx variables are shared and may be accessed directly between all running script instances in Bayonne. The values stored in a global are also persistent for the duration of the server running.
In addition, DSO based functions will create variables for storing results under the name of the function call, and the DSO lookup module will create a %lookup object to contain lookup results. Also, server initiated scripts can pass and initialize variable arguments. For example, the fifo ``start'' command may be passed command line arguments, and these arguments then appear as initialized constants when the new script session is started.
While most symbols are stored as fixed length strings, there are a number of special types that exist. "typed" symbols are not typed in the same way traditional typing occurs, however. "typed" symbols are typically symbols that perform automatic operations each time they are referenced as a command argument.
The ``counter'' symbol is used as a typed symbol that automatically increments itself each time it is referenced. This can be useful for creating loop or error retry counters.
The ``stack'', ``sequence'', and ``fifo'' are symbols that can hold multiple values. A stack releases values each time it is referenced in lifo order until it is empty again. A fifo does this in fifo order. The sequence object repeats its contents when reaching the end of it's list. Values are inserted into each of these types by using the special ``post'' keyword. ``stack'' and ``fifo'' may be used as global. objects to create and script ACD-like functionality in Bayonne.
In addition to type behavior, symbols have properties that may be associated with them. These include a number of default properties, and some that may be available through the loading of ccscript packages. A symbol's property may be extracted by adding a .property to the symbol name. One such property is the symbol "type". Hence, to access the symbol type of %myvar in a script command argument, one might use ``%myvar.type''. The following script properties are defined:
property | requires | description |
.type | - | base symbol type in scripting |
.length | - | length of symbol contents |
.size | - | size of symbol entry |
.count | - | number of members in array or stack |
.max | - | maximum size of array or stack |
.value | - | computed integer value of symbol contents |
.bool | - | compute bool value of contents |
.dtmf | (bayonne) | returns dtmf representation of contents |
.upper | .use string | returns upper case text of contents |
.lower | .use string | returns lower case text of contents |
.capitalize | .use string | returns capitalized text of contents |
.trim | .use string | returns contents without lead or trailing spaces |
.url | .use url | decode url escaped content |
.bin | .use url | decode binhex content |
.each | .use digits | extracts content into a , seperated list of chars |
.date | .use date | returns date encoded values from content |
.year | .use date | returns integer year of a date variable |
.month | .use date | returns integer month of a date variable |
.weekday | .use date | returns weekday of a date variable |
.monthof | .use date | returns named month of a date |
.day | .use date | returns integer day of month of a date |
.time | .use time | returns time encoded values from content |
.hour | .use time | returns hour value from time |
.minute | .use time | returns minute value from time |
.second | .use time | returns second value from time |
Similarly, some properties may be used to set variables of a specific type or to convert the values stored in a variable through a set command into a specific format. These are done by using ``set.xxx'' where xxx is a specific property format. For example, to url encode a string, one can use ``set.url %mystr ...contents...''. A date variable to store the current date may be similarly created with ``set.date %myvar''.
While not meant for intensive algorithmic work, Bayonne's scripting does support a concept for both handling arrays and lists. An array is considered a numerically sequenced list of variables with access controlled by a common .index property. A list is a token packed list of character strings.
There are also other ways of creating array "effects". One way to do this is to use symbol indirection. Symbol indirection means the symbol that is referenced is found by examining the contents of another symbol. For example ``sim 1'' string will be logged after execution of this fragment:
set %myref "mysim1" set %mysim1 "sim 1" slog @myref
When using lists, Bayonne assumes by default that these are comma seperated content in a symbol. However, characters other than commas may be used. A list may be examined in a variety of ways. Using the ``string'' package, it is possible to build, extract, and organize the contents of lists. The ``foreach'' keyword can be used to look through the contents of a list. In each case, a special token= attribute may be used to specify the seperator character being used if it is not a comma.
The default list seperator token, ``,'', is stored in a special variable, %script.token. You may modify this single character variable to another token. The special .n properties can be used to access a part of a token seperated list in a manner similar to how members of an array are accessed. List members are numbered from 1, so xxx.4 will access 4-th member of xxx list.
Starting with ccScript 2.4.4, it is also possible to compute a variable hash as part of a symbol name reference. For example, a variable named ``%var#hash'' will be expanded to use the contents of %hash to compute the name as a componentized name. Hash tables in this form may appear in either local or global storage, and hence provide a means to componentize local scope symbol names and store stack local arrays.
Session ids are symbol values that are used to refer to a Bayonne port that is running a call script. These references are used so that scripts in one port can, when needed, identify and reference scripts running in another port. The most common example of this is the start command, which can return a variable that will hold the session id of the script and port that a script was started on. This can then be used to rendezvous two script sessions for a ``join''. Similarly, the child script could also examine its %session.parent to find out who started it to join.
The most basic reference id is simply a port number. This reference is a numeric id, and is the same as the value that %driver.id returns. The disadvantage of using port numbers is that they are not aware of call sessions. Imagine I start a script on another port, say ``3'', and I decide to join to it later. In the interum, the call that was originally started has disconnected, and an entirely new call has appeared on port 3. I do not want to join to this new and unrelated call.
The second form of a session id is 14 bytes long. It is composed of a ``-'', a port number, another ``-'', and a call unique timestamp. This is known as a local session id. Many generated ids, such as those stored in %session.pickupid and %session.joinid, are passed in this form. This assures that the reference is not just to a specific port, but also to a specific call that is occuring on that port.
The third form of a session id starts with a node name and a ``-'', followed by a local session id. This form is considered unique for all call sessions on all server instances, assuming each server has a unique call node. These are best used when resolving activities that will be spread over multiple servers, such as call detail that may be collected into a single database.
The event flag is used to notify where a branch point for a given event occurs while the current script is executing. Events can be receipt of DTMF digits in a menu, a call disconnecting, etc. The script will immediately branch to an event handler designated line when in the "top" part of the script, but will not repeatedly branch from one event handler to another; most event handlers will block while an event handler is active.
The exception to this rule is hangup and error events. These cannot be blocked, and will always execute except from within the event handlers for hangup and/or error themselves. Event handlers can be thought of as being like "soft" signals.
In addition to marking script locations, the script "event mask" for the current line can also be modified. When the event mask is modified, that script statement may be set to ignore or process an event that may occur.
The following event identifiers are considered "standard" for Bayonne:
identifier | default | description |
^hangup or ^exit |
detach | the calling party has disconnected |
^error |
advance | a script error is being reported |
^dtmf |
- | any unclaimed dtmf events |
^timeout |
advance | timed operation timed out |
^0 to 9, a to d |
- | dtmf digits |
^pound or \verb star= |
- | dtmf "#" or "*" key hit |
^tone |
- | tone event heard on line |
^notify |
advance | notify unsolicited event |
^part or ^cancel |
detach | conference/join disconnected. |
^fail or ^invalid |
advance | failed process |
^event |
- | event message received |
^child |
- | notify child exiting |
^pickup |
- | we are picked up by another session |
^answer |
- | call progress caller answered |
^busy |
- | call progress dialed line busy |
^noanswer |
- | call progress call timed out |
^time |
- | call timer event trap |
Some of these script events also have Bayonne ccScript variables which are set when they occur. When an event occurs and there is no handler present, very often execution simply continues on the next statement, but the variable that is set may still be examined. The following event related symbols may be referenced:
%script.error | last script ``error'' message. |
%pstn.tone | name of last telephone tone received. |
%session.eventsenderid | trunk port that sent an ^event to us. |
%session.eventsendermsg | event message that is being sent. |
%session.joinid | trunk port we last joined with. |
%session.notifytext | text of unsolicited external notify |
%session.notifytype | short description of external notify |
With the introduction of ccScript 2.5.6, it has become possible to use arbitrary
``named'' event references as well as traditional xxx handlers in ccScript. Named
event handlers allows one to define an arbitrary named event in Bayonne and to
offer an optional script handler to receive the event when Bayonne requests the event.
Named events are designated differently than traditonal handlers. They may use either
the symbol, or may be enclosed in a pair of 's. The latter might be used if the
named event contains spaces or other non-parsable characters.
Each named event is typically composed of a two part name. The first part represents the
event classification, and the second part is the actual event. A ``:'' is used to
seperate the two parts. Hence, a typical named event handler tag will look like
xxx:yyy.
Named events may be used much the same way that traditional event handlers are used.
They may be listed together and or'd, or even listed together with traditional event
handlers. The latter allows named events to substitute for dtmf keyboard navigation, and
allows, for example, the asr system to bind words to dtmf navigation. Consider, for
example:
... say "press 1 for yes or two for no" listen count=1 timeout=30s ^1 @asr:yes redirect ::doit ^2 @asr:no redirect ::dont
In this case, the asr word recognizer is pushed from the listen command as descreat words
which are directly associated with dtmf digit events. If either the key is pressed or
the word is recognized, the event handler is requested. Furthermore, named events that
appear after a xxx handler also inherit the same signal masks as the xxx handler.
The following named events are commonly used:
digits:xxx | digit pattern to match |
digits:expired | collect command timed out |
digits:partial | digits partially matched a pattern |
digits:invalid | digits failed to match any pattern |
dial:reorder | dialing reorder tone heard |
dial:nodialtone | no dialtone in dial command |
dial:noringback | no ringback heard |
dial:noanswer | nobody answered call |
dial:busy | caller was busy |
dial:answer | answer detection confirmed |
tone:xxx | some tone event detected |
asr:xxx[,yyy] | a asr word or phrase was heard |
error:xxx | a specific error event occurred |
event:text | an inter-port send text message event |
parted:xxx | join parted reason event (to be added) |
Of all the named events, ``digits:'' events are treated very special. These are used
to impliment dialing plans. The actual digits are matched from time a new dtmf digit is received. The match can be done not just by absolute values
but also by patterns. Several special characters are reserved for this:
The ``N'' character is a place holder for any value of 2-9, and the ``X'' character is
a placeholder for any decimal value. In addition, the letter ``O'' can be used for a
lead one which is ignored if not present, and ''Z'' may be used for a lead zero which
is also ignored if missing. Finally, the digit pattern can be made to only be in effect
for a specific country location, using a countrycode/. The effective country code is
found in %session.dialing. For example, a PBX application which needs to dial numbers
for north american dialing plan when used in the US, and for different numbers in
"country 389" (Macedonia) could be represented, with lead '9' for outside line select,
with 911 service if only in US, and with Macedonian Emergency numbers, as follows:
@digits:389/92 # police @digits:389/93 # fire @digits:389/94 # ambulence @digits:1/911 # quick shortcut @digits:1/9911 # pbx dial "9" first mode... redirect ::emergency @digits:389/99 @digits:1/011 @digits:1/9011 redirect ::international @digits:1/9ONXXNXXXXXX # outside number in north american dialing plan @digits:389/9XXXXXXX # Macedonian cities are 9x followed by 6 digits? redirect ::outsidedial
This allows one to create scripts that can be localized easily, even with very complex
dialing plans. The digits: events can be used during any dtmf input, including
collect.
Scripts can be broken down into blocks of conditional code. To support this, we have both if-then-else-endif constructs, and case blocks. In addition, blocks of code can be enclosed in loops, and the loops themselves can be controlled by conditionals.
All conditional statements use one of two forms; either two arguments seperated by a conditional test operator, or a test condition and a single argument. Multiple conditions can be chained together with the ``and'' and ``or'' keyword.
Conditional operators include = (or -eq) and <>
(or -ne), which provide
integer comparison of two arguments, along with >
, <
, <=
, and >=
, which
also perform comparison of integer values. A simple conditional
expression of this form might be something like if %val < 3 ::exit
,
which tests to see if %val is less than 3, and if so, branches to ::exit.
Conditional operators also include string comparisons. These differ in that they do not operate on the interger value of a string, but on it's effective sort order. The most basic string operators include == (or .eq.) and != (or .ne.) which test if two arguments are equal or not. These comparisons are done case insensitive, hence ``th'' will be the same as ``Th''.
A special operator, ``$'', can be used to determine if one substring is
contained within another string. This can be used to see if the first
argument is contained in the second. For example, a test like ``th $
this'' would be true, since ``th'' is in ``this''. Similar to perl, the
`` '' operator may also be used. This will test if a regular expression
can be matched with the contents of an argument. To quickly test the
prefix or suffix of a string, there is a special $<
and $>
operator. These check if the argument is contained either at the start or
the end of the second argument.
In addition to the conditional operators, variables may be used in special conditional tests. These tests are named -xxx, where ``-xxx argument'' will check if the argument meets the specified condition, and ``!xxx argument'', where the argument will be tested to not meed the condition. The following conditional tests are supported:
conditional | description |
---|---|
-defined | tests if a given argument is a defined variable |
-empty | tests if the argument or variable is empty or not |
-script | tests if a given script label is defined |
-module | tests if a specific .use module is loaded |
-voice | tests if a given voice exists in prompt directory |
-altvoice | tests if a given voice exists in the alt prompt directory |
-sysvoice | tests for a given system voice library |
-appvoice | tests for a given application voice library |
-group | tests if a specified trunk group exists |
-plugin | tests if a specified plugin is loaded |
-service | tests if the service level is set to a specific value |
-dtmf | tests if a specific dtmf ``option'' setting is in effect |
-feature | tests if the feature specified in argument exists |
-ext | whether the argument refers to a valid extension number |
-station | whether refers to a station port extension number |
-virtual | whether refers to a virtual extension entity |
-user | whether the argument refers to a user profile id |
-dnd | whether dnd is set for the extension argument |
-hunt | whether the argument refers to a hunt group |
The -feature option can test for a number of features. The features you can test for include ``-feature join'' to test for join support, ``switch'' for pbx support, ``spans'' for digital span support, and various audio capabilities.
The ``if'' expression can take three forms. It can be used as a ``if ...expr... label'', where a branch occurs when an if expression is true. it can be in the form ``if ...expr...'' followed by a ``then'' command on the following line. The then block continues until an ``endif'' command, and may support an ``else'' option as well. This form is similar to the bash shell if-then-fi conditional. Finally, if the conditional is needed for only one statement, there is a special case form that can be entered on a single line, in the form ``if ...expr.. then command [args]'', which allows a single statement to be conditional on the expression.
The ``case'' statement is followed immediately by a conditional expression, and can be used multiple times to break a group of lines up until the ``endcase'' is used or a loop exits. The ``otherwise'' keyword is the same as the default case in C. A set of ``case'' expressions and ``otherwise'' may be enclosed in a ``do-loop'' to get behavior similar to C switch blocks.
The ``do'' and ``loop'' statements each support a conditional expression. A conditional can hence be tested for at both the top and bottom of a loop. The ``break'' and ``continue'' statements can also include a conditional expression.
In addition to ``do-loop'' there is ``for-loop'', ``foreach-loop'' and, with ccscript 2.5.2 and later, ``fordata-loop''. The for statement assigns a variable from a list of arguments, much like how for works in bash. foreach can be used to decompose a token seperated list variable. fordata is used to perform a ``read'' statement from data statements (such as a #sql query result) directly in a loop. In all cases, break and continue can still be used within the loop.
Bayonne recognizes the idea of symbol scope. Symbol scope occurs when refering to variables that are either ``global'' in scope, and hence universally accessable, or that are ``local''. Local symbols exist on a special heap, and a new local heap is created when a subroutine level is called.
Global scope symbols are those that have componentized names. Hence ``%xxx.yyy'' is treated as a global symbol. Local symbols do not have componentized names. Hence ``%yyy'' is a local scope symbol. This allows one to determine scope purely from symbol name, rather than requiring implicit commands to create a symbol that is local or global.
Bayonne scripting recognizes subroutines as invoked through either the ``gosub'' or ``call'' script statements. When a ``call'' statement is used, execution is transfered to the given script, as a subroutine, and that script can then return to continue script flow with a ``return'' statement.
When invoking a subroutine through ``call'', it is possible to specify if a new local variable heap will be created. If a new heap is created, local variables are created in the context of the subroutine only, and any changes are lost when ``return'' is used to return back to the calling script. The ``return'' statement can be used to transfer values, typically from a subroutine's local heap, back to a variable in the calling script's context. This is done with ``var=value'' lists that may follow the return statement, as in ``return status=ok'', for example.
Subroutines may also be invoked with paramatric paramaters. These paramaters are then inserted into the local heap of the newly called subroutine and become accessible as local variables. This also is done with keyword value pairs, as in ``call ::mysub myvar=3'', for example. When this is done, a local constant is created, known as %myvar, that is then usable from ::mysub, and exists until ::mysub returns. Since this is a constant, its value may not be altered within ::mysub.
Sometimes a subroutine can contain initialization values to use if no parametric value have been passed. Since parametric values are constants, they cannot be altered, and hence, one can do something like:
::mysub const %myvar 4 ...
And thereby define %myvar locally as 4, unless there was a ``call ::mysub'' with an alternate value being passed as a myvar=xxx.
Subroutines also support call by reference. This can be used to permit a subroutine to directly modify a local variable in the scope of the calling script automatically. Call by reference is done by using a keyword=&var form of keyword. Consider the example:
... set %mysym "test" call ::mysub myref=&mysym slog %mysym ... ::mysub set %myref "tested" return
In this case, the slog will show ``tested'' since %myref in ::mysub actually points back to %mysym in the calling script.
It is possible to call a subroutine that uses the same local variables directly, rather than having it create a new local context. This can be done either using the ``source'' statement, or ``call.local''. It is also possible to have a subroutine that has no local variable context, and hence always refers to the base global context. This is done with a ``call.global''.
In addition to subroutines, loops, and conditional statements, scripts may be gathered together under transaction blocks. Normally each script statement is step executed over a timed interval. This is done to reduce loading when deriving several hundred instances of Bayonne for a high density voice solution. However, some scripts either involve statements that are trivial or that need to be executed together. These can be done using a ``begin'' and ``end'' block.
When a transaction block is used, all the statements within it are executed as quickly as possible as if they were a single script step. This allows one to go through a series of set or const definitions quickly, for example.
In addition, ``begin'' may be used in front of a cascading case block, or before an ``if'' statement. This allows all the conditional tests within the case or if to be executed together until the ``endif'' or ``endcase'', rather than depending on stepping.
Transaction blocks only work for statements that do not involve scheduled operations. Things that schedule include sleep, playing and recording of audio, and libexec statements. When these appear within a transaction block, the transaction block is suspended for those specific statements, and then resumes on the next unscheduled statement.
Transaction blocks will automatically exit when a branch statement is enountered, or when a ``end'', ``endif'', ``loop'', or ``endcase'' is encountered. Transaction blocks cannot encapsulate a loop, and they will not operate with a subroutine call since calling a subroutine is a branching operation.
In the default configuration supplied with 1.2.0, there are three places that audio prompts may be played from; a subdirectory of /var/lib/bayonne, a subdirectory of /usr[/local]/share/bayonne, and a subdirectory of /home/bayonne. /var/lib/bayonne is meant to hold audio that is manipulatable and recorded samples. /usr/share/bayonne includes both system and language specific prompts that are supplied by pre-installed or packaged Bayonne applications. /home/bayonne is meant to store both scripts and audio prompts that are locally created by the system administrator for site specific or custom applications.
To refer to an audio sample stored in a /var/lib/bayonne/xxx subdirectory, all one needs to do is refer to the partial path as a filename, as in xxx/yyy.au. The play and record command, and others, also support a ``prefix='' option, which can be used to specify the xxx subdirectory name seperately. This may be convenient when audio prompt filenames would need to be otherwise constructed from concatenated symbols or strings. The special preprocessor directive, .prefix, or .dir may be used to assure that the xxx subdirectory exists in /var/lib/bayonne. Hence, if we wish to record messages into /var/lib/bayonne/msgs, we can use either something like ``record prefix=msgs myfile'' or ``record msgs/myfile''.
For audio that is stored in /var/lib/bayonne, a file extension is automatically added if none is specified. This default file extension is set in the [script] section of bayonne.conf, and is stored in %audio.extension. The default extension is typically set to .au, for sun audio files. However, you can either modify %audio.extension in a running script, and many commands allow you to override the default extension. For example, to record msgs/myfile as a .wav file, this could be done with ``record prefix=msgs extension=.wav myfile''. The extension can also be specified directly as part of the filename, as in ``record msgs/myfile.wav''.
When using the ``prefix='' modifier, there is a special prefix with a pre-reserved meaning. When using ``prefix=memory'', rather then using audio for /var/lib/bayonne/memory, the audio files are actually stored on the tmpfs, in ram (or swap), which usually is /dev/shm. This can be used to record special prompts that maybe need to be processed further. It is particularly useful when constructing a rotating ``feed'' which may be recorded from one channel and listened to in realtime by multiple callers on other channels.
When using simple filenames, as as ``test'' or ``1'' in ``play'' or ``speak'' commands, a lot of special magic occurs to determine where the audio file will be played from. The first consideration is the current voice setting. Since audio samples are split into language specific voice libraries, each with a seperate subdirectory in /usr/share/bayonne, the directory looked at is the one that the current voice library is set for. The default is usually UsEngM, and this is set in the [script] section of bayonne.conf as well.
In addition to voice libraries, each application may have a seperate subdirectory for it's own prompts under each voice. The application subdirectory is the same as the base name of the script file (or the effective name if it has been .exported). Hence, if we have an application, "myapp.scr", and we try to use ``play myprompt'', then, the first place examined would likely be /usr/share/bayonne/UsEngM/myapp/myprompt.au. If there is no application specific directory, then the main voice library directory is used. Hence, if we use ``play 1'', and there is no /usr/share/bayonne/UsEngM/myapp subdirectory, then instead we will retrieve and play /usr/share/bayonne/UsEngM/1.au.
The current voice libary may be found in %session.voice. While %session.voice may be changed directly, the ``options voice=xxx'' script command is usually used to select a new voice because it will also select the correct phrasebook language module to use with that voice. The default extension of voice library prompts is controlled by the driver, and is usually either .au, for sun audio encoded mu-law files, or .al, for raw a-law encoded copies of .au content. Furture drivers may also make use of .gsm or .adpcm for pre-encoded audio prompts.
Audio may be played directly from a URL when XML support has been enabled in Bayonne. URL audio is played at the moment only from a http: source. hence, one can do something like ``play http:/audio/myaudio.au''. If XML support is not enabled, then `http:' prompts are ignored.
In addition to standard URL's, there are a number of special pseudo-urls's that have special meanings. These are in the form of xxx:yyy, where xxx is a special url code, and yyy is an autoprompt, or pathname.
The following special url codes are defined:
alt:
This redirects prompt selection to /home/bayonne. From that point, the
actual prompt file is then selected based on the same voice library
and script path selection rules as if it had been taken from a standard
prompt stored through the default path. Hence, a subdirectory of
/home/bayonne named after the current voice library will be used.
app:
This refers to selecting audio from a subdirectory named the same as
the application script name from a voice library subdirectory from
/usr[/local]/share/bayonne.
audio:
Somewhat related to music:, the audio: name refers to any
.au or .al file that has been stored in /usr/[local]/share/bayonne/audio.
These audio samples can be any generic audio file that may be desired,
including sound effects, etc.
mem:
This has the same effect as using the ``prefix=memory'' option, and refers
to audio stored or retrieved from system memory as organized by the tmpfs.
The tmpfs usually is mounted as /dev/shm.
music:
This references audio samples stored as digital music. Digital music is
typically used for playing of ``music on hold''. Digital music is stored
in /var[/local]/share/bayonne/music. It is stored in 8 bit encoded u-law
(and optionally in a-law) since 8bit 8khz files are actually smaller than mpeg
encoded high fidelity audio, and because the telephone network does not
support higher quality audio in the first place.
sys:
This refers to audio prompts stored in the non-language specific
``system'' voice library. These are found in
/usr[/local]/share/bayonne/sys.
tmp:
This refers to audio that is stored (or recorded to) through the /tmp
filesystem. This allows use of /tmp for audio storage.
usr:
home:
This refers to audio stored in the alternate audio prefix (altprompts in
bayonne.conf). The alternate audio prefix is normally set to
/home/bayonne. The actual file will either be loaded from an application
specific subdirectory of /home/bayonne if a subdirectory exists to match
the script filename, or directly from the /home/bayonne directory itself.
var:
This refers to a subdirectory of /var/lib/bayonne that is named the same
as the current script file. This provides a quick way for a script to
organize it's recordable prompts under the same subdirectory of the
datapath.
xxxx:
Any reference to a url that does not use any of the special keywords is
used to refer directly to a subdirectory of the system prompt directory.
This is often used to organize system audio prompts used in system support
scripts, such as playrec. The actual location is found from
/usr[/local]/bayonne/sys/xxxx/.
xxxx::
There are a number of other odd forms of pathnames used to select prompts
based on script names or so called export names. This translates roughly
to a language localized prompt based on the voice library name that is
found in /usr[/local]/share/bayonne. This allows the xxxx name to be used
in place of the name of the current script file.
/
When bayonne is used to host user driven scripts, this is done in a manner
somewhat anagelous to how apache hosts the / path and user specific
content. The audio prompt for these are assumed to exist in a
subdirectory of a user's home account, usually from $HOME/.bayonne.
Based on ccscript 2.5.0 and later, there are two types of pre-processor directives; those that are compile control directives, and those that involve compile-time symbol or token substitution. Compile control directives start with ``.'' and are on a seperate line, along with any options or arguments, such as filenames. An example of a compile control directive is ``.include xxx'', which can be used to insert include files.
Compile time symbols are given a value at the time the script is compiled, and may be considered like a constant. Some compile time symbols are constants, and may be used to represent values found in the [script] section of bayonne.conf. For example, to insert the definition of ``timeout'' that is found in [script], one might refer to it within ccScript as ``$timeout''.
With ccScript 2.5.0, we introduced three special compile time symbols to help with debugging. These are ``$script.name'', ``$script.file'', and ``$script.line''. The first refers to the name code is currently being compiled under. With the use of the ``.export'' directive, this may in fact be different than the filename, which may be found with ``$script.file''. The final symbol expands to the current statement line number being compiled.
The following pre-processor directives exist:
.code
.data
.map
Define the next section of text in your source file either as script
(code), as a local embedded data block for use with a ``read'' statement,
or as a special embedded data block for use with the ``map'' statement.
.default[nn] name [default-value]
This creates a definition for a user preference entry. User
preference entries are stored under /var/lib/bayonne/users/xxx,
and contain persistent user information such as account passwords
or telephone extension settings. The default value, if present,
is used to specify the default to fill a new user preference
record for this named entry when it is created. These may be
accessed through the %user. global depending on the login state
of a given call session.
.define name value
Used to define the value of a pre-processor symbol that can then
be referenced under $name.
.export name
This is used to export the remaining ::xxx script script sections
as if they were compiled from a script source file that is named
by the directive rather than the one they are compiled from.
Hence, the sections are compiled as name::xxx rather than the
source file name that they are compiled from. This can be used
to override or insert a section script into another script file.
.global[nn] name [default-value]
Introduced with Bayonne 1.3.x, the global directive allows a
script to define a initial value if it has no value as yet. This is useful to both
create globals of a specific size and assure they have some
initial value before scripts are ran.
.include filename ...
Insert text from another source file. This can be used to insert
constants such as .define statements from a seperate header
file, for example. The include directory is assumed to be the
same as the script source directory, although one can specify
relative paths for subdirectories.
.hunt[nn] name [default-value]
This is used to create persistent hunt group entries and
definitions that are used for pbx hunt groups, and that
are saved under /var/lib/bayonne/hunting/xxx. These are
usually extrated through the huntinfo command in scripting.
.initial[nn] name value ...
This is used to define script global symbols that need to be
of a known size and initialized to a known value at the start
of each call session before scripts are ran. If you have a
.initial statement in test.scr for a variable named
save, for example, a global variable named %test.save
will be initialized for each call.
.languages language ...
Load Bayonne phrasebook module for a specified language if it is
not already installed because the current script depends on it.
.line[nn] name [default-value]
This is used to create an entry for port (line) persistant
entries that may be used to save line properties that are
modifyable. These are typically accessed through the %line.
variables.
.module modulename ...
Load the specified Bayonne ``module'' (.mod) if it is not already
installed in memory because the given script depends on it being
active.
.prefix subdir ...
.dir subdir ...
Create the specified subdirectory in /var/lib/bayonne if it does
not exist because the current script requires it to store audio
data.
.private
Define any ::xxx scripts compiled from this point forward as
being private and only referencable from within the current script
file. This means one cannot use yyy::xxx to invoke or reference a
local script. The .public directive can be used to
re-enable public access to compiled scripts in the current file.
This directive only effects the current file, and is reset to
public when the next source file is compiled.
.public
Mark any scripts compiled from this point forward as being public.
This is used to re-enable yyy::xxx scripts if a .private
directive has been used.
.requires commandname
Tests to see if a specified and required script command exists in
the current system. If the required script is not found, then the
current file cannot be compiled, and the system halts.
.rpc rpcname
Bind the next script statement as a RPC named script service to be
invoked through Bayonne rpc services under the specified name.
This assumes the script returns a status message back to the rpc
server.
.rule rulename rule
.english rulename rule
.lang... rulename rule
Define a phrasebook rule in your sourcefile. This allows
in-source definition of rules that your script may use in the
``speak'' command, rather than having to define them seperately
in a config file. Rules that apply to all languages are defined
with a single bf .rule directive, while language specific
varients, if needed, can also be specified by using the language
name as a ``.'' preprocessor directive.
.safe
.unsafe
This is used to mark sections of code whether they should report
compile time syntax errors or not. Unsafe code will report no
error, and may be used when loosly binding optional script
keywords that work only when specific modules are present.
.template templatefile
This allows a foreign script file to specify the event handlers
that will be defined and used for the current script. This allows
one to define a template script for common event handling shared
among a number of common scripts.
.use packagename ...
Install a generic ccscript package module that the current script
requires, if it is not already installed. These are normally
found in /usr/lib/ccscript2.
.xml xml-modulename ...
Load the specified Bayonne XML support module (,xml) if it is not
already installed.
These commands describe the various means to create or initialize a
symbols in the scripting language. Symbols may be of specialized types
that automatically perform operations when referenced, or generic symbols
of a specific type or size.
alias %name value
Create an alias symbol name that points to a real symbol name when
referenced. The alias and target must be in the same scope, hence
local aliases cannot reference global objects in subroutines.
array[.size] count [values...]
array size=size count=count %var [value ...]
Create a ccScript array composite object. This creates a special
%var.index to select a specific array element, and a bunch of
seperate variables named %var.1 through %var.count, one for
each array element.
cache[.size] count %var [value ...]
cache size=size count=count %var [value ...]
Create a ccScript fifo cache variable. This creates a variable
that logs and returns content in reverse order.
clear %var ...
Clear (empty) one or more variables. It does not
de-allocate. This means that if you need to determine whether
a variable is ``there'' or not in a TGI script which is passed
the variable, the empty string is equivalent to a nonexistent
variable.
const %var values...
const var=value ...
Set a constant which may not be altered later. Alternately
multiple constants may be initialized.
counter %var
Create a variable that automatically increments as it is referenced.
fifo[.size] count %var [value ...]
fifo size=size count=count %var [value ...]
Create a ccScript fifo ``stack'' variable. This creates a variable
that automatically unwinds from first in to last in when referenced.
init %var values...
init var=value ...
init[.min|.max] [size=bytes] %var [=] values...
init[.size] %var [=] values...
init[.property] %var values
Initialize a new system variable with default values. If the
variable already exists, it is skipped. Optionally multiple
variables may be initialized at one. Can also init a variable
to a minimum or maximum value of a set of values. A property
plugin may also be initialized, such as initing a ".date" or
other specialized plugin type value. The "xx = expr" form
can be used to initiate a numeric expression with basic
+, -, *, and / operators.
lifo[.size] count %var [value ...]
lifo size=size count=count %var [value ...]
stack[.size] count %var [value ...]
stack size=size count=count %var [value ...]
Create a ccScript ``stack'' variable. Stack variables are lifo
objects and unwind automatically on reference.
list[.size] [size=bytes] [token=char] %var values...
List is used to quickly construct a token seperated packed
list variable which may then be used with the foreach loop,
referenced through the packed property index, or manipulated
with the string command.
ref %ref components ...
This can be used by a subroutine to form a local instance of a
reference object that points to a real object in the public name
space by building a target public object name from components that
are then glued with ``.'' notation. This is different from an
alias since a simple named alias can only reference the local
scope in a subroutine.
sequence[.size] count %var [value ...]
sequence size=size count=count %var [value ...]
Create a ccScript repeating sequence variable. This repeats content
of a sequence in the order set.
set %var values...
set var=value ...
set[.min|.max] [size=bytes] [justify=format] %var [=] values...
set[.size] [justify=format] %var [=] values...
set[.property] %var values
Set a variable to a known value. Can also set multiple variables.
Can also set a variable to the minimum or maximum value of
a set of values. A property plugin may also be directly set, such
as initing a ".date" or other specialized plugin type value. The
"xx = expr" form can be used to set a symbol based on a
simple numeric expression using basic +, -, *, and / operators.
A left, right, or "center" justification may also be used. Please
note, in the future, the overloaded expression form may be
seperated into a new "number" and "float" keyword.
size space %var...
Pre-allocate "space" bytes for the following variables.
While set is perhaps the most common way to both define and manipulate a symbol, there are a number of additional script commands that can be used for this purpose.
arm %var
Arm a variable so that it will auto-branch if modified.
dec[.property] %timevar [offset]
Decrement a variable, perhaps with a specified offset,
otherwise by "1". When used with packages that set
numeric properties, such as the ``date'' and ``time''
package, dec can adjust dates and times as well.
disarm %var
Disarm an armed variable.
dup %var %dest
Duplicate an existing object into a new one.
inc[.property] %timevar [offset]
Increment a variable, perhaps with a specified offset,
otherwise by "1". When used with packages that set
numeric properties, such as the ``date'' and ``time''
package, inc can adjust dates and times as well.
post %var value ...
Post one or more additional values into a stack, fifo, sequence,
or cache.
swap %var %var
Exchange the contents of two variables with each other. They
must both be of the same scope.
remove %var value ...
Remove a specific value entry from a stack, fifo, sequence, or
cache variable.
dirname %var
Much like the shell dirname, to extract a directory name
from a path.
basename %var [extensions...]
Reduce a variable to a simple base filename, and strip any
of the optionally listed extensions from it.
fullpath %var fullpath
If %var is a partial pathname, then merge it with the passed
path to form a complete pathname.
There are a special category of script commands that directly deal with and manipulate the script images in active memory. These include commands that operate on scripts by creating local template headers, or user session specific copies, of loaded scripts, which can then be selectivily modified in some manner. The following script manipulation commands exist:
begin
end
Mark the start or end of a script transaction block.
disable name=label trapids...
This is used to disable specific ^...
handlers within a live
script. The script, when used, behaves as if there never was
a ^...
handler programmed for it. The enable command can
be used to make the handlers active again.
enable name=label trapids...
This is used to enable specific trap handlers within the body of
a script which may have been disabled.
gather %var suffix
Gather the number of instances of a given xxx::suffix scripts
that are found in the current compiled image, and store the
list of named scripts in the specified %var.
lock[.wait|.unlock] id=value
Attempt to create a global lock under the specified id. If
successful, then no other active call will be able to claim
the same global lock until the current call session releases
it, either through a lock.unlock or by exiting. If .wait is
used, this command will block until the lock is aquired. Global
locks could be used for pin numbers, account ids, etc.
break [value op value][ and | or ...]
Break out of a loop. Can optionally have a conditional test (see
if).
call[.public|.protected] value [var=value ...]
gosub[.public|.protected] value [var=value ..]
Call a named script or event handler as a subroutine. If the
call is successful, an optional list of variables can be
conditionally set. ``.public'' can be used to specify that
the subroutine is ran without a local stack, and ``.protected''
can be used to share the current stack with the subroutine
directly rather than having it create a new local context.
Passed arguments are set in the local context of a new subroutine
as constants. It is also possible to pass variables by reference
to a subroutine.
continue [value op value][ and | or ...]
Continue a loop immediately. Can optionally have a conditional
test (see if).
case [value op value][ and | or ...]
otherwise
endcase
The case statement is a multiline conditional branch. A single
case (or otherwise) line can be entered based on a list
of separate case identifiers within a given do or for loop. The
otherwise statement is used to mark the default entry of
a case block.
do [value op value]
Start of a loop. Can optionally have a conditional test (see if). A
do loop may include case statements.
exit [exit-code]
Terminate script interpreter or invoke the special ``::exit''
label in the current script. If the script is called from an
external script or service service, then a exit result may
also be posted back to the external script. If the script was
started by another port, then that port will receive a child
event and also receive the exit-code specified.
for %var values...
Assign %var to a list of values within a loop. A for loop may
include case statements. For is similar to it's behavior
in ``bash''.
fordata source|table=source %var ...
Read ``data'' statements starting from a script referenced as
the ``source'' as part of a loop. The loop will exit when all
data lines have been read. This command could be used to create
a simple loop to read #sql query results, for example.
``fordata'' was introduced in ccscript 2.5.2.
foreach[.offset] %var %packed
Assign %var from items found in a packed list. An optional
offset can be used to skip the first n items. A foreach loop
may include case statements.
goto label [var=value ...]
Goto a named script or event handler in a script. If the
goto is successful, an optional list of variables can be
conditionally set.
if value op value [and | or ...] label [var=value ...]
Used to test two values or variables against each other and
branch when the expression is found true. There are both
"string" equity and "value" equity operations, as well as
substring tests, etc. Optionally a set of variables can
be initialized based on the conditional branch. Multiple
conditions may be chained together using either and or
or. In addition to simple values, ()'s may be used
to enclose simple integer expressions, the results of which
may be compared with operators as well.
if value op value [and | or ...] then [script command]
Used to test two values or variables against each other and
execute a single statement if the expression is true. Multiple
conditions may be chained together using either and or
or. In addition to simple values, ()'s may be used
to enclose simple integer expressions, the results of which
may be compared with operators as well.
if value op value [and | or ...]
then
else
endif
Used to test two values or variables against each other and
start a multi-line conditional block. This block is enclosed
in a ``then'' line and completed with a ``endif'' line. A
``else'' statement line may exist in between.
label value ...
Create a named local label to receive a skip command request.
loop [value op value][ and | or ...]
Continuation of a for or do loop. Can optionally have a
conditional test (see if).
on trap label
Allows one to test for a previously blocked signal and see if
it had occurred. If the signal had occurred, then the branch
will be taken.
once label
Within a single script, once is guaranteed to only goto a new
script (like a goto) "once", which can be used as protection
agains recursive invokation.
pop
Pop a gosub off the stack. This only has an effect when
returning.
repeat count
Repeat a loop for the specified count number of times.
return[.exit] [label] variable=value ...
Return from a gosub. You can also return to a specific label or
^handler
within the parent script you are returning to. In
addition, you can use return to set specific variables to
known values in the context of the returned script, as a means to
pass values back when returning. Finally, the .exit option may
be used to exit the script if there is no script to return to.
select value match label [..match-label pairs] [var=val...]
Select is used to branch to a new script based on matching a value
or variable to a list of possible values. For each match, a different
script or event handler may be selected. Options include
"select.prefix" to match string prefixes, "select.suffix" to match
by tailing values only, "select.length" to match by length,
and "select.value" to match by numeric value. If a branch is
found, an optional list of variables can be conditionally set.
skip [label]
Skip may be used alone to skip over a case statement. This is
similar to what c/c++ does with cases if no intervening break is
present. Since ccScript automatically behaves as if break is
present, skip is needed to flow into another case. The second
use of skip is to branch to a local label as defined by the
label keyword.
source label
Source is used to invoke a subroutine which uses the current
stack context, and is somewhat similar in purpose and effect
to ``source'' in the bash shell.
try lables... [var=value ...]
Attempt to goto one or more labels. If the label is not found
in an existing script, the next one in the list will be tried.
If any branch attempt is successful, then optionally variables
may also be set.
tryeach[.offset] %packed [var=value ...]
Attempt to branch to a script based on the values held in a
packed list. Each item will be tried in term, starting from
the offset if one is specified. If a branch point exists
an optional list of variables may be conditionally initialized.
GNU ccScript supports some basic manipulation of embedded data tables within a script. This has been extended in Bayonne to support retrieval of multi-row queries that can then be examined in scripting.
data values...
data.mapindex label [variable=value] ...
This can be used to embed compile time data tables into a script
file. Some modules (such as sql query) generate data dynamically
for the interpreter and then embed the results also as data
statements. the data statements can then either be mapped or
read by referencing the script label they appear under. Each
data statement line is treated as a seperate row.
map[.prefix|.suffix|.value|.absolute] [table=label] value
Map allows branching to a data.mapindex entry in the specified
label, or within the current script label if none is specified. A
match, either by value, or from start or end, may be made of the
given value, and if a matching data.mapindex entry is found, then
the label specified is branched to, with the optional initialized
variables, as if a goto had been used.
read [table=source] [row=row] [col=offset] %var ...
Read a row (line) of data from a data statement, either from the
next row of the current table source, or from a specified table
source or row offset. The row is read into variables, one for
each column. A column offset may be used to start from a
specified column offset. Data sources are always referenced by
a script label. The special ``#'' script label (and ``#xxx''
entries) refer to Bayonne's dynamic script buffer when XML support
is enabled, and where query results of various types may be stored.
slog [.debug
.info .notice .warning .err .crit .emergency] message...
Post a message to the system log as a syslog(3) message. The
logging level can be specified as part of the command. If no
logging level is specified the message will be logged as
slog.notice.
If Bayonne or the stand-alone ccScript interpreter are running on a console or under initlog(8), the messages will be output on the standard error descriptor, not standard output. Note that you cannot use % characters in the strings to be outputted.
A number of interesting script commands are available through the use of external ccscript "packages". These are typically saved in /usr/lib/ccscript2, and are installed into the interpreter with the .use command. The following extension based commands can be available:
chop[.offset] [offset=bytes] %var bytes
This is used to chop out a specific count of digits from within
a string. This is available with the ``digits'' package.
delete[.offset] [offset=bytes] %var values...
Delete specific digit values from the specified variable if they
exist at the offset specified. For example, to remove a lead
``1800'' from a phone number, one might use:
``delete.0 %phone 1800''. If the specified pattern is not
found, it is not removed. The offset can be a number, or
``end'', to specify removal of values from the end of the
%var involved. This is available with the ``digits'' package.
insert[.offset] [offset=bytes] %var value...
Insert digit values starting at a known offset in an existing
variable. This is available with the ``digits'' package.
prefix[.offset] [offset=bytes] %var value...
If the specified prefix value does not exist at the specified
offset, then it is inserted. This is available with the
``digits'' package.
random.range [seed=seed] [count=count] [offset=offset] [min=value] [max=value] [reroll=count] %var ...
The ``random'' package offers a fairly complex number of options
for creating or storing pseudo-random digits into symbols. These
include things that simulate various dice behavior, such as a
known sum (count) of a known range of values, and even the ability
to specify minimum or maximum values that can be generated.
random.seed seedvalue
Seed the pseudo-random number generator. This is used with the
``random'' package.
replace[.offset] [offset=bytes] %var find replace ...
This is used to replace a specific digit pattern with a new value
if the digit pattern is found at the specified offset in %var.
This is available with the ``digits'' package.
scale.precision scale %var...
The ``scale'' package enables basic floating point multiplication.
This can be used to rescale a known variable by a floating point
value, and storing the result to a known digit precision. For
example, to scale a variable by 40%, we could use:
``scale.2 0.4 %myvar''.
sort[.reverse] [token=char] [size=bytes] %var
The ccScript ``sort'' package allows one to sort either
packed string arrays, which use a token to seperate content,
or the contents of a ``sequence'', ``stack'', or ``fifo''.
Sorting can be in forward or reverse order.
string.cut|.chop [token=char] [offset=items] %string %var ...
This is functionally similar to string.unpack except that
as each item is unpacked into a target variable, it is also
removed from the original packed list.
string.pack|.clear [token=char] [offset=item] %string [size=bytes] [prefix=text] [suffix=text] [mask=format] [fill=text] values...
Create a packed string from a list of items, using the specified
token to seperate each item (or the default token, ','). If the
string already exists, you can pack items from a specified offset.
If the variable doesn't exist, the size can be used to specify
the size of the new variable. Prefix and suffix allow each item
to have a prefix or suffix in addition to the token. An existing
variable may also be appended to or cleared. A special mask may
also be used to preformat data in a specific way, such as to
filter and field numeric values, zero fill, etc. A fill string
can be specified to fill to the remaining end of the object. This
keyword is supported with the ``string'' package.
string.unpack [token=char] [offset=item] %string %var...
This can be used to unpack a tokenized string into seperate
variables. One may specify the item number to start from rather
than the start of the list. This is available with the ``string''
package.
trim[.offset] [offset=bytes] %var bytes
This is used to trim leading and trailing digits outside of
the range of count and offset specified. It's the inverse of
chop. This is available with the ``digits'' package.
This covers the basic set of call processing script command extensions that are common and are generally usable with all Bayonne drivers. Some of these commands may depend on specific bayonne plugins or extensions to be installed.
answer [rings [ringtime]] [fax=label] [station=id]
answer [maxRing=rings] [maxTime=ringtime]
Answer the line if it has not been answered yet. Optionally wait
for a specified number of rings before answering, and allow a
maximum inter-ring timeout before considering that ringing has
"stopped". If the rings are stopped before the count has been
reached, then the call is treated as a hangup (abandon). If a
fax tone is detected, it is possible to branch to an alternate
label, as well as to present a fax station id, assuming the
driver supports fax.
append [prefix=path] [file=target] [voice=voicelib] [extension=ext] [offset = offset] prompts...
Appendseries of audio files or prompts into an existing
audio file on subdirectory of /var/lib/bayonne. The append
command processes filenames in a manner similar to play to
reference audio from the voice library, and will do basic audio
format conversions.
audit[.log|.clear|.post] key1=value1 key2=value2 ...
Declare or post a CDR record. When a cdr record is declared,
it is written when the call terminates using the audit plugin.
When posted, it is written immediately through the audit plugin
to a seperate auditing data spool. If a cdr record had been
set earlier, it may be cleared with .clear, in which case no
cdr will be posted when the call session terminates.
build [prefix=path] [file=target] [voice=voicelib] [language=translator] prompts...
This command constructs a new audio output file using the
phrasebook rules. This can be thought of as functionally
similar to the ``speak'' command, but where output is used
to create a new audio prompt rather than to be spoken
immediately.
busy.port|.group|.span|.card id=ports
This is an admin priviledged command that is used to busy out
a series of ports other than the current one. The effect of
busy can be countered with applying the idle command to the
same ports.
cleardigits[.count] [label [var=value ...]]
Clear a specified number of digits from the dtmf input buffer, and
then optionally branch to a specified label as if a goto
statement has been executed. In place of count, one can use
``.all'', to clear all digits from the input buffer or ``.last''
to clear just the last input digit. Furthermore, ``.pop'' may
be used to pop off the first input digit only, and ``.trap''
may be used to perform dtmf trap handlers after branching or
within the current script.
collect[.clear|.trim] digits [timeout [term [ignore]]]
collect[.clear|.trim] [var=&sym] [count=digits] [exit=term] [ignore=ignore]
Collect up to a specified number of DTMF digits from the user.
A interdigit timeout is normally specified. In addition, certain
digits can be listed as "terminating" digits (terminate input),
and others can be "ignored". The .clear option can be used to
clear the input buffer before collecting, otherwise any pending
digits in the dtmf session buffer may be processed as input prior
to waiting for additional digits. The .trim option can be used to
strip out any additional digits that may still be in the buffer
after collection count.
control command [arguments]
A priviledged command which allows a script to directly insert any
valid fifo control command to the Bayonne server.
copy [prefix=path] [file=target] [voice=voicelib] [extension=ext] [format=format] prompts...
Copy a series of audio files or prompts into a new destination
audio file on subdirectory of /var/lib/bayonne. The copy
command processes filenames in a manner similar to play to
reference audio from the voice library, and will do basic audio
format conversions.
create[.user|.hunt] id
Create a user or hunt group preference record for the specified
id. This writes a new record filled with default values.
debug message..
Send a message to the debugging monitor if one is installed
through the plugins.
delete[.user|.hunt] id
Delete a user or hunt group preference record of a specified id.
The preference entry is removed and deactivated from system
preferences.
dial [timeout=cptimeout] [origin=telnbr] [prefix=prefixcode] number...
This performs dialing with something that is a standard
international number, as in "+1 800 555 1212", for example. These
can be in symbols, or other places, and are dialed on the public
network as a network number through the driver. Normal numbers
may also be passed, and either may appear in a symbol, as a
literal, or composed from multiple values.
dial.dtmf|.pulse|.mf [prefix=digits] number...
This is used to perform ``soft'' dialing operations, which are
used to emit dtmf digits and special dialing characters directly
as synthesised audio with timed pauses, and to do so without any
call progress detection.
dial.int|.nat|.loc [timeout=cptimeout] [origin=telnbr] [prefix=prefixcode] number...
This makes use of the trunk group definitions of special prefixes
and codes for international, national, and local dialing, to
produce a final valid phone number. The phone number is then
dialed through the network, and call progress is used to determine
the results.
erase [prefix=path] filename
Erase a specified file from a /var/lib/bayonne prefixed path.
examine[.ext|.trk|.tie] id=callid %var
This is used to copy the contents of variables from another
call session into the local call session. The use of .ext, .trk,
or .tie occur only when used with a PBX capable driver.
fileinfo prefix=path file=name [extension=.ext] [offset=>offset] [annotation=>save]
This command is used to examine audio files that have been
recorded by Bayonne or submitted from other sources. It is
capable of returning both the total number of samples contained
in the audio file specified, and any annotation that may be
stored within it.
flash offtime ontime
flash offhook=offtimer onhook=ontimer
Flash the line a specified number of milliseconds (offtime) and
then wait for ontime. If dialtone occurs, then may be used as
a branch.
hangup[.self]
This is essentially the same as the ccScript ``exit'' command.
hangup.port|.span|.card|.group id=ports
This is used to hangup on an active call on another port. A
specific port or a specific group of ports may be specified.
idle.port|.group|.span|.card id=ports
This is an admin priviledged command that is used to reset to
idle a series of ports other than the current one.
libexec[.once|.play] timeout program [query-parms=value ...]
Execute an external application or system script file thru the
Bayonne TGI service. This can be used to run Perl scripts,
shell scripts, etc. A timeout specifies how long to wait for
the program to complete. A timeout of 0 can be used for
starting "detached" commands. Optionally one can set libexec
to execute only one instance within a given script, or use
.play to run an external tgi that will generate an audio file
which will then be played and removed automatically when the
tgi exits.
move [prefix=path] source destination
Move or rename an individual file in the /var/lib/bayonne prefixed
path.
options option=value ...
The options command is an odd command. It can include common
options that affect script processing, and also options that may
be driver specific. An option is set by option name, with a
specified value. The common generic options include dtmf, which
can specify dtmf handling, either as on, off, line (default), or
script global. Another important option is result=, as this is
used to send a result back from a rpc initiated script to a
web service. The logging= option sets the default logging level.
The options keyword can also set a new voice, and when voice= is
used, the correct phrasebook language module is also activated.
[alt]play[.any|.all|.one|.tmp] [prefix=path] [offset=samples] [limit=samples] [gain=db] [pitch=freq-adjust] [speed=slow|fast] [volume=%vol] [text=message] [voice=voicelib] [extension=fileextension] audiofile(s)
Play one or more audio files in sequence to the user. Bayonne
supports raw samples, ".au" samples, and ".wav" files. Different
telephony cards support different codecs, so it's best to use
ulaw/alaw files if you expect to use them on any Bayonne server.
Optionally one can play any of the messages found, or only the
first message found, or a temp file which is then erased after
play. The altplay version of this command is used in
conjunction with say, and plays only if there is no
tts system installed.
record[.append] [prefix=path] [gain=db] [volume=%vol] [trim=samples] [minSize=samples] [text=message] [maxTime=maxrectime] [exit=termdigits] [encoding=audio-format] [annotation=text] [extension=fileext] [save=savename] [offset=samples] audiofile
Record user audio to a file, up to a specified time limit, and
support optional abort digits (DTMF). Optionally one can
append to an existing file, or record into part of an existing
file by offset. Record with save= option means the file is saved
or moved to the specified name if recording is successful,
replacing what was previously there.
record [prefix=path] [maxTime=timelimit] frames=count filename
This version of record is used to create a looping audio feed that
can be used to share an audio source with multiple listeners on
different call sessions.
redirect[.digitcount] label
This is a special marker token. When a ^handler
is followed
immediately by a redirect statement, the specified number of
digits are cleared from the input buffer, and the script branches
immediately to the specified label, rather than step executing.
This allows for an immediate branch execution, rather than the
extra step delay required when a goto immediately follows a
^handler
. The redirect command also behaves as a cleardigits
command.
route[.table] [type=table] values...
This is used to match the list of values against named events,
either in a specific ``table'', or the default route: table. When
the default ``route:'' table is used, digit matching and country
code dialing rules apply just like for named ``digits:'' events.
However, ``route:'' entries may branch on PARTIAL matches, and
hence it is important to list your route events in order, with
the shortest matches listed first, and exact matches listed last.
say [gain=db] [volume=%level] [voice=ttsname] text...
If there is a tts module installed in Bayonne, or an external one
has been made active, then the say command may be used to generate
synthesised speech. This command is ignored if there is no tts
service present, and so may be used in conjunction with
altplay and altspeak.
send.copy id|gid|ext|trk|tie=id %var...
This is used to copy the contents of current variables into the
global variable space of another selected call session.
send.digits id|gid|ext|trk|tie=id digits...
This is used to send digits into the %session.digits input
buffer of another call session, to thereby act as if dtmf
digits were detected.
send[.message] id|gid|ext|trk|tie=id message=text
This is used to post a message from the current call session to
either a specific call session by id, or to a series of active
call sessions under a common trunk group. The message recipient
branches to a ^event
handler, receives %session.eventsenderid
with the id of the sending call, and %session.eventsendermsg with
the message text.
send.post id|gid|ext|trk|tie=id %varname value ...
This is used to post values into the contents of variables in
another call session.
send.signal port [msg]
This is used to send a signal event to a port that is currently
waiting for a join. Signals are specifically used to cancel a join
request and are ignored otherwise if no join is pending. The msg
is passed as the reason why the join failed for the other side.
If an ^event
handler is present on the other side, it will be
activated.
send.status msg
This is used to send a message or status update back to the rpc
server from the current script.
service level
This is an admin priviledged command which is used to set the
service level of the server as a whole, either up, down, or
under a special service condition.
sleep timeout [rings]
sleep [maxtime=timeout] [maxRing=rings]
Sleep a specified number of seconds. Can also sleep until the
specified number of rings have occurred. Millisecond timeouts
may also be specified using ms, as in ``100ms''.
[alt]speak[.any|.all] [language=langmodule] [voice=voicelib] [gain=db] [pitch=adjust] [speed=fast|slow] [volume=%level] [text=message] [extension=fileext] phrasebook-rules words...
Used to implement phrasebook rules based prompt generation. The
altspeak version exists to speak a phrase only if there is
no tts system installed, and is meant to be used after a say
command. The current voice library and language module options
may be used, or new ones may be specified on the command line for
the current command only.
start[.offset|.group|.ext|.trunk|.tie|.span|.wait] [var=&symbol][maxTime=timeout] [submit=vars-to-copy] [expire] offset|group script [parms]
start a script on another trunk port as offset from the current
port %id or by issuing a request against another trunk group.
Hence "start 1 test" starts script test on the port next to the
current one, for example. Normally, a large offset like
"start 24 test" might be used to start a script on the next
T span. Start can start a script immediately, or time delayed as
a queued request with the timeout specified in maxTime. A
``var=&varname'' can be used to save the started session id to a
variable.
statinfo id=groupname [capacity=&var] [incoming=&var] [outgoing=&var] [used=&var] [avail=&var]
This command is used to collect active call statistics from a
known trunk group entry. This can be used to determine how many
calls are in process for a given group, for example. The
specific named stat item entries are stored into specified
variable names that are passed as part of keyword symbols.
sync.exit [time=seconds]
Set the exit timer for this call session based on the start time
of the call. If no timeout is specified, then the exit timer is
cleared. When an exit timer expires, and there is no ^time
handler, the call session exits.
sync.start|.current time=seconds]
This is used to set a call event timer, which will invoke the
^time
handler in a script, at a specified number of seconds
from the original start time of the call, or for a number of
seconds after the current time. If no time is specified, then
any previously set timer is cleared.
sync time=duration [maxRing=rings]
Sleep the current call until the total time since the start of
call is equal to the specified duration. This is like a sleep
call, but scheduled from start of the call rather than from
the current time. A version of sleep.start may be added later
to also do this.
tone [count=repeat] [timeout=intertone] name
Play a named tone, as defined in the bayonne.conf file. The tone
can be repeated a specified number of times.
tone [count=repeat] [timeout=intertone] frequency=freq [amplitude=amp] length=duration
Play a dynamically constructed monofrequency tone on the fly.
tone [count=repeat] [timeout=intertone] freq1=freq freq2=freq [ampl1=amp] [ampl2=amp] length=duration
Play a dynamically constructed dual frequency tone on the fly.
These commands deal with management of various persistant databases and the concept of user sessions in Bayonne. User sessions are based on the idea that a given call session may be logged in under a specified login id. Login id's may be used for any purpose, including PBX extensions, voice mailboxes, pins for debit systems, etc.
change id=usertag value=newvalue
Change a user property in the preferences database for the
currently logged in user to the specified value. The actual
change is stored with commit.
commit
Commit may be used to commit changes made to user preferences
through the change or password command so that they are
permenantly stored. This only is effective when logged in under
a user id session.
huntinfo id=pilot tag=&var ...
While hunt groups are primarily used in scripting PBX systems,
they could be used generically in bayonne for other purposes. A
hunt group is a persistant data record under a known pilot number
who's script defined tags and values may be extracted with a
simple script interface.
login id=userid password=password
Attempt to set the current call session under a specified user
id. This will be successfull if the correct password is used.
logout
Logs out of the current active user session.
password [id=userid] password
This is used to change the password of the preference and login
for the currently logged in user id. If the current user is
priviledged, it may change the password of other users as well.
reset id=usertag
Resets a user property in the preferences database for the
currently logged in user to it's default. The actual change
is stored with commit.
userinfo id=userid infokey=&var
This command is used to extract information about foreign user
id's that are stored in the preference system. The currently
logged in user's info is accessed through %user.xxx.
There are a number of Bayonne ``features'' which are not universally available. Their use may depend on the existance or deployment of optional servers, and/or may depend on extended support in each driver, which only some telephony drivers will provide.
asr[.type|.cancel] options
The ``asr'' command initializes an instance of a speech
recognition system as supplied by an external server. The
options are passed to the asr engine as command line options
would be. The most common use is ``asr.sphinx ...'', which
initializes an instance of sphinx through the ``giza'' server.
The asr must be initialized before it may be used. The asr
command can be used multiple times to initialize under
different configurations. The ``asr.cancel'' command can be
used to release asr services for the port when they are no
longer needed.
listen first=seconds next=seconds var=&var
The listen command will work with an initialized asr system
to ``listen'' for spoken words. The first timeout is used to
specify the wait time for the first utterence, and the next
timeout is used to specify the wait time until no more
utterences will be received. The var= is used to save received
utterences into a comma delimited list which can then be parsed
with a foreach loop.
The exact availability and behavior of a number of script commands do depend on specific features or capabilities that must be provided for by specific telephony drivers. These capabilities and features may not be universally available since some drivers will be missing features or capabilities that might be found in others. These driver specific Bayonne script commands are described here:
accept label
Some Bayonne cti drivers support ISDN (pri) hardware where call
accepting may be manually controlled. When call accpetance is
manually controlled, intercept messages may be played back to
users, and these are unbilled. The billing clock is only enabled
from the perspective of the telco if the call has been accepted,
and this can be done with the accept command. The accept command
then branches to a new script because the ^hangup
handler of this
type of script must use reject to reject the call if he has
hung up before it has been accepted.
answer.intercom|.trunk|.parent|,pickup label
Drivers with PBX support have an enhanced version of the answer
command that enables answer to be used as a reply to inter-call
session intercom dialing and pickup requests. This is used to
notify the intercom dialer or pickup requestor that an answer
response has occured on a given extension or line. On successful
answer completion, a branch is taken.
dial.intercom ringback=tone count=rings [transfer=referer] [name=display] station ...
Drivers capable of PBX support introduce a new and special form
of the dial command. This special dial is used for intercom
dialing, and supports the idea of being in a special intercom
dial state. The extension engaged in intercom dialing receives
an artificial ringback signal, and each station in the possible
list of stations is dialed until it is picked up, or the specified
count of rings has been waited.
join[.hangup] id=port [waitTime=retrytimer] [maxTime=totallimit]
This is the join command represented in most telephony drivers
which have either TDM support or the ability to do soft joins.
Join attempts to connect the channel to the port specified in a
private full duplex conversation. If .hangup is used, then both
ends hang up (exit) when the join completes. A join is normally
a one time join attempt, but may be retried over a time interval
if a waittimer is specified. The value of %script.error is set
based on the reasons that join was parted. The .parent option
refers to the call session that started this one if it happened
through a start command. Other options, such as .pickup and
.intercom, are only available in PBX capable drivers.
join.parent|.transfer|.pickup|.recall [waitTime=retrytimer] [maxTime=totallimit]
This version of join joins to trunk call state session identifiers.
.parent refers to the %session.parent, while the others are are
used in PBX drivers only.
pickup.incoming|.hold|.intercom|.trunk|.parent|.recall label
Pickup is used to send messages to a specified port identifer
or incoming call source that are translated to ^pickup
handlers
which may then be answered or joined to. This can be used to
interupt a voice mail session when the station user originally
called picks up the line, for example. This feature is only
available in PBX drivers.
reject
This is used in conjunction with the accept command in a
script, and is usually used as a ^hangup
handler, as it rejects
the call, terminating it without billing. See accept for
further details.
ring.start [group=trunkgroup] [source=othercallsession] extensionids...
This is a PBX driver feature to initiate station ringing on
behalf of another trunk or station. Source can be used to
indicate the ring is being started on behalf of another
station, and hence acts as an alternate means of doing a blind
transfer. A trunk or port group may also be used or referenced
to do ringing for a group.
ring.stop [group=trunkgroup] extensions...
This is a PBX driver feature to turn of station ringing for the
specified stations.
ring.clear
This is a PBX driver feature to clear all pending ring requests
against the current station.
wait[.hangup] [id=session] maxTime=waittimer
This is the generic version of wait used by all drivers with
TDM or soft join support, A station that is waiting can wait
for a join either from any station that attempts to join it, or
from a specific station. It can also wait up to a specific
time interval for the join to occur. The .hangup option is
used to indicate hangup will occur when the join is over.
Otherwise %script.error will hold the reason that the join ended.
wait.parent|.transfer|.pickup|.recall maxTime=retrytimer
This version of wait waits for a join from a specific trunk by
the call state session identifiers. .parent refers to the
%session.parent, while the others are are used in PBX drivers
only.
There are a number of Bayonne script commands that are based on or involved with the presense of XML support, when it has been enabled for bayonne. A number of specialized plugins also exist and may be used only when XML support has been enabled. The effected script commands include:
assign var=name [size=bytes] [value=value]
While the assign command is built into Bayonne regardless of
whethere XML support is enabled or not, it is most often used
for supporting XML plugins which contain scripting languages that
need to set or modify session symbols. It may be viewed as an
alternative to set.
bayonnexml url=http:xxxx [submit=vars] [maxTime=timeout]
When the BayonneXML plugin is installed, the bayonnexml command
can be used as a convenient shortcut script command that sets
the xml parser to the BayonneXML dialect, and then performs a
http ``get'' request to retrieve a BayonneXML document from a
web server, using the query variables passed in submit. If such
a document is successfully retrieved and parsed, then it is
executed.
dir[.reverse] prefix=subdir [var=&count] [extension=fileext] [match=prefix]
The directory plugin creates a special dir command which
can be used to scan the contents of a specified subdirectory
from /var/lib/bayonne. The contents are returned as multi-row
data that can then be examined with the read command. The
columns returned includes filename, sample size, and annotation
for audio files. Matches can be done by name prefix and/or by
specific file extensions. A variable can be specified to receive
the total directory count.
sql [query=string] [maxTime=timelimit] query...
Issue a SQL query through the selected sql plugin driver. The
driver returns a multi-row data result that can then be examined
with the ``read'' command. The data tuples are in table source
``#sql'' and the header can be read from ``#header''.
In the past, Bayonne scripts were executed as sessions within the
Bayonne server engine itself. Each script was tied to a specific
telephone port, and ran in direct control of a telephone session. With
Bayonne 1.3.0, a new external scripting shell was introduced. This
external shell allows one to create scripts that manage one or more
telephone calls externally from outside the Bayonne server. External
scripting also introduced support for creating web page and email
output.
External scripts can be located anywhere on the file system, and may be
executed directly with the ``bayonne -shell'' command. Command line
arguments are parsed into a %argv.x array, with %argv.count holding
the total argument count. The ``environment'' is received as %env.xxx
and may be directly accessed in this way as well. External scripts can
be invoked manually, or a scheduler such as from a crontab or even soon
from the Bayonne scheduler itself.
Any number of external ``scripts'' can be launched concurrently, as each
is a seperate shell process. Each instance launched believes it is
running as a script named ``shell''. Hence .xxx variables will always
expand to %shell.xxx.
In addition to providing command line arguments, external scripts, when
invoked from a web server as a .cgi file, will parse and store headers
in ccscript global variables. These are stored in %http.xxx for http
headers that are received, and the query string from the server get
method is also decoded, into %query.xxx. At the moment only ``get''
methods are supported, not ``post''. This allows one to write a
ccscript that is invoked by a web server as a cgi which both generates
output back to the browser, and manipulates bayonne.
To support output generation, the external shell has additional commands
to both output headers, and html/xml formatted text. In addition,
external scripting can mine encode data and create encapsulated messages
for delivery through email. This allows external scripting to be used
for email delivery of voice content. Finally, external scripts can
themselves be ran from "tgi" libexec calls and they will receive the
same information from the environment into the scripting system that
tgi modules provide for other scripting languages.
While external shell scripting has many commands in common with server
driven scripting, there are some commands which behave differently, and
other commands that are unique to external scripting. There are also
many low level telephony manipulation commands that do not at the moment
exist in external scripting, though they may at some future point
through support for external shelled tgi.
These commands only exist in external scripting and may not be used in
bayonne server scripts.
attachment[.type] audiopath
This is used to attach the contents of a named audio file in the
user (/var) data area into an email document as part of a multi-
part (VPIM) encoded message. The audio file is given a valid
header based on the ``type'' (``name'' for a recorded users
name, ``forward'' for a prefix recorded for a forwarded message,
or default voice message body) and inserted as a base64 encoded
stream. The attachment command may also be used to create various
other kinds of mime attached documents.
btag.name [attr=value ...]
This is used to mark the start of text that is held within a
xml/html ``tag''. For example, to insert a href into the
output, one would use something like: btag.a href=``myurl''
and this would create a <a href=``myurl''> entry.
compile
This will cause the bayonne server to re-compile all server
scripts.
echo[.text] text...
This simply echo's output to the user unmodified. If the
.text option is used, then no nl is inserted. A nl will
be put in front of echo automatically if a previous header was
sent.
endmail
Stop sending output to ``sendmail'' and resume output on the
local console or web server.
etag.name
This is used to mark the end of a tag that was started with
btag. It would generate the ``
header[.title] text...
Output a ``header'' field to stdout. This is used to write
http response headers back to a http server, or to generate
the header for an email document. If the .xxx title option is
used, then header will automatically create a XXX: ... formatted
header and take the text as the content behind the XXX:, rather
than requiring this to be done manually.
restart
This tells the bayonne server to restart.
sendmail [address...]
Send all further output to a pipe to the ``sendmail'' command.
This allows the user to construct headers and email content
using external scripting.
tag.name [attr=value ...] [text ...]
This is used to create a complete stand alone tag entry in
output. For example, to create a <br/> tag, one might
use tag.br. It can also enclose text in a tag. For example,
to create a url mail reference in one line, one can use:
tag.a href="mailto:..." "send email" and get something like
<a href="mailto:...">send mail</a> automatically created.
test[.script] port(s)...
This is used to initiate a Bayonne ``test'' script from test::xxx on the ports
that are specified. Test scripts are used to run automated tests inside the
Bayonne server that are externally started.
These commands have the same name and generally the same function as
those found in internal server scripting, but have somewhat different
behavior and effects, as noted here:
sleep maxTime=timeout
The shell version of sleep cannot be interrupted until the
sleep time period has been expired. In server scripting,
sleep may be interrupted by telephony events, may support
ring counts, and is generally used for supporting a broader
range of functionality.
The collect command adds to %session.digits, it doesn't overwrite it. Make sure that you're clearing %session.digits before each collect (unless you really do intend to append).
Don't use '=', use '-eq' to check for equality. Also, '==' is broken in older versions of Bayonne. Use '.eq.' instead.
Are you confusing the name of a script (like ``foo'') with a label name (like ``::foo'')?
Remember that the pound sign is used as a comment character. Things like ``dial #'' don't work because ccscript thinks you're starting a comment. Quote the ``#'' character instead.
Make sure you are using the *::foo syntax when playing prompts, and that you have %application.language set properly. ``play foo'' is almost certainly not going to do what it looks like it should do. Use ``play *::foo'' instead.
Make sure that if you use a variable returned by a TGI script that the TGI script defined it. Otherwise bayonne dumps core (as of 0.6.4).
Did the ccscript engine print out any interesting error messages during startup or 'bayctrl compile'? Perhaps you should review them.
Did you remember to run 'bayctrl compile' or to restart bayonne after you modified your script?
If you do things like "goto script", and script.scr looks like this:
::start do stuff do stuff ^event ^event goto script
The goto will fail. Instead, say "goto script::start".
Make sure that after you deal with an event, the script jumps somewhere. If the path of execution falls off the bottom of the file (or hits another label), then the script engine will jump back to the beginning of the file (or the current label) ad infinitum. Keep in mind that you are developing a telephony application, and you must be constantly interacting with the user or they think you've hung up on them.
When jumping as the result of a conditional (like "if %return -eq 1 goto main"), you don't say ``goto''. State it in the form "if %return -eq 1 main". The goto is implied after the if conditional.
If you're using Perl and it's DBI module for doing database accesses through TGI, here's one way you can retrieve data from the database via fetchall_arrayref(). The syntax seems to be easily forgettable for some reason.
$ref = $sth->fetchall_arrayref(); $row0_col0 = $$ref[0][0]; $row1_col1 = $$ref[1][1]; $row0_col1 = $$ref[0][1];
The standard way to get digits so the caller can interrupt the message is:
clear %session.digits play *::1 # "Press 1 for foo, press 2 for baz, # press 3 for gronk..." sleep 60 ^dtmf collect 1 5 "*#" "ABCD" if %session.digits -eq 1 ::label1 if %session.digits -eq 2 ::label2 goto ::invalid
The standard way to get digits so the caller can't interrupt the message is:
clear %session.digits play *::1 # "Press 1 for foo, press 2 for baz, # press 3 for gronk..." collect 1 5 "*#" "ABCD" if %session.digits -eq 1 ::label1 if %session.digits -eq 2 ::label2 goto ::invalid
* A note on event traps:
They are order sensitive. If you have
^dtmf goto ::foo ^pound goto ::bar
You will never be able to reach bar. ^dtmf
takes precedence. Also, traps do not work within traps.
^dtmf ^star goto ::foo ^pound goto ::bar
Will not work. Dtmf detect is always turned off in the script step following a dtmf trap, with the exception of the collect command.
It's a good idea to document your TGI return values in your program header. Make a template for all your TGI programs and stick to it. Make sure there's a section for the return values in the headers and use it. One convention seen around the OST code is to use 1 for a successful call, 0 for an unsuccessful call, and -1 for an internal script error.
Remember that the value of the %return variable is persistent. If you aren't careful, your TGI scripts will fall through without setting a return value. This is especially annoying if you forget to set a return value which means "operation successful" If you don't see a line like this in the server logs:
fifo: cmd=SET&2&return&1
Then your TGI script isn't setting a return value. The ccscript that's executing your TGI will then use the return value from the last ccscript you executed, which is just hours of debugging fun. Especially when one of your TGIs is working just fine (but doesn't set a return value) and your ccscript checks the return value to see if an error occurred, and guess what, it's the return value from the TGI script you called before the current one. Chances are that that return value doesn't have anything to do with the return value from the TGI script you just executed, which leads to very confusing results.
Document your database schema. Make sure that you put the column indexes into the database schema document, and you include a Big Fat Warning that tells any potential modifiers of said document that if they touch the document, they get to audit any database access code that uses hard-coded column indexes. The idea is that if they change the database schema, those column indexes may no longer be valid. An even better solution (if your TGI language supports it) is to define a set of symbolic constants for the database columns in one file and include the constant definitions in all the database access code.
Phrase rules can be placed in bayonne.conf proper under the appropriate language and in application specific conf files as found in /etc/bayonne.d. English "rules" are found under section [english] in the .conf files, for example.
Phrasebook prompts are used to build prompts that are effected by content. Lets take the example of a phrase like "you have ... message(s) waiting". In english this phrase has several possibilities. Depending on the quantity involved, you may wish to use a singular or plural form of message. You may wish to substitute the word "no" for a zero quantity.
In Bayonne phrasebook, we may define this as follows:
in your script command:
speak &msgswaiting %msgcount no msgwaiting msgswaiting
We would then define under [english] something like:
msgswaiting = youhave &number &zero &singular &plural
This assumes you have the following prompt files defined for your application:
The speaking of numbers itself is held in the default Bayonne distribution, though the default prompt list can also be replaced with your own. Rules can also appear "within" your statement, though this generally makes them non-flexible for different languages.
Speaking of currency "values" have specific phrasebook rules. Currency values are also subject to the "&zero" rule, so for example:
balance=youhave &cy &zero remaining
and using:
speak &balance %balance nomoney
can use the alternate "no monay" .au prompt rather than saying "0 dollars".
&number | speak a number unless zero |
&unit | speak a number as units; zero spoken |
&order | speak a "order", as in 1st, 2nd, 3rd, etc. |
&skip | skip next word if spoken number was zero. |
&ignore | always ignore the next word (needed to match multilingual). |
&use | always use the next word (needed to match multilingual). |
&spell | spell the word or speak individual digits of a number. |
&zero | substitute a word if value is zero else skip. |
&single | substitute word if last number spoken was 1. |
&plural | substitute word if last number spoken was not 1. |
&date | speak a date. |
&day | speak only day of the week of a date. |
&weekday | speak the current day of the week. |
&time | speak a time. |
&primary | speak primary currency value (dollar(s) and cent(s)) |
&local | speak local currency |
&duration | speak hours, minutes, and seconds, for duration values. |
&cy | speak default currency (either primary, local, or both) |
Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.2 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts
This document was generated using the LaTeX2HTML translator Version 2002 (1.62)
Copyright © 1993, 1994, 1995, 1996,
Nikos Drakos,
Computer Based Learning Unit, University of Leeds.
Copyright © 1997, 1998, 1999,
Ross Moore,
Mathematics Department, Macquarie University, Sydney.
The command line arguments were:
latex2html -nosubdir -split 0 script.tex
The translation was initiated by Luca on 2003-08-24