Can you make functions within the dialplan?

I have never worked with a procedural programming language before and it’s somewhat unsettling.

Here is what I’m trying to write in C++.

#include<iostream>
using namespace std;

void sayNumber(int a); //function says a number
int randNumb(); //function generates a random number
void sayRand(); //function takes both functions above and runs them together

int main() 
{
  sayRand();
  return 0;
}

void sayNumber(int a) 
{
cout << a; //SayNumber(${a}) 
return;
}

int randNumber() 
{
foo = rand() % 10 + 1; //RAND (1, 9)
return foo;
}

void sayRand()
{
int bar = randNumber();
sayNumber(bar);
return;
}

The best I can come up with in dialplan is:

[test]
exten => foo,1,NoOp()
same => n,Set(j=${RAND(1,2)})
same => n,Set(k=${j})
exten => bar,1,NoOp()
same => n, Set(l=8)
GotoIf(${j}=1?foo:bar) 
;--I want this to be a switch statement were k or l is printed based on the result of j but I have no idea how to make this work
--;
same => n,SayNumber(${k}) ;This would be under foo
same => n,SayNumber(${l})   ;This would be under bar

Lastly can you use variables to navigate the priorities?
For example does something like this work:


exten => foo,1,NoOp()
same => n,Set(i=3) 
same => ${i},SayNumber(${i})

Your “function” doesn’t meet the mathematical definition of a function, or the definition used in Asterisk. Asterisk does support subroutines. Examples of using these are in the sample extensions.conf.

You don’t seem to ever call your “function”, and, from the code you have provided it looks like what you want is neither a function nor a subroutine, but a scoped block.

The syntax for defining symbolic labels is also illustrated in the sample configuration file.

Well yea its not a real function it’s just abstract code.
I was trying to ask how to declare a function with a function inside it and how to call the initial function within a dialplan. I should have been more clear. I know exactly what I want to do in C++, and I have no idea how to make it work in this language.


#include<iostream>
using namespace std;

void sayNumber(int a); //function says a number
int randNumb(); //function generates a random number
void sayRand(); //function takes both functions above and runs them together

int main() 
{
  sayRand();
  return 0;
}

void sayNumber(int a) 
{
cout << a; //SayNumber(${a}) 
return;
}

int randNumber() 
{
foo = rand() % 10 + 1; //RAND (1, 9)
return foo;
}

void sayRand()
{
int bar = randNumber();
sayNumber(bar);
return;
}

The only examples on the entire internet are using asterisk per-defined functions or doing extremely simple things like this single example from the wiki:

same = n,Set(DB(testfamily/testkey)=Alice)
same = n,Dial(PJSIP/${DB(testfamily/testkey)})
same=n,Gotoif($[${DB_EXISTS(testfamily/testkey)}]?keyexists:keydoesnotexist)
same = n,Log(NOTICE, Deleting the key testfamily/testkey which had the value:${DB_DELETE(testfamily/testkey)})

I can’t find examples that don’t use per-defined functions and it’s a black hole of having to figure out what 20 different pre-built functions do to be able to understand what the sample code is suppose to be explaining about functions.

[test]
exten => foo,1,NoOp() ;declares a function, foo
same => n,Set(j=${RAND(1,2)}) ;sets j to a random number
same => n,Set(k=${j}) ;sets k = j j and k should be local scope to foo (i think?)
exten => bar,1,NoOp() ;declares second function bar
same => n, Set(l=8)  ;sets l = 8, should be local scope to bar
;after this I'm completely lost
;how do I close a function?
;if I use goto am I going to just be jumping backwards in the code?
;I have no idea how to separate out a function and not have it run or call it.

Note that AEL provides a more structured language, that compiles into dialplan. However, you should also note that few people use it, so getting support for it may be difficult.

Asterisk doesn’t support user defined functions, only subroutines, but you don’t need true functions for Turing completeness. The C family use “function” to cover a concept that is very un-function like, in that its value is always empty, and it is used for its side effects.,

Your example fragment seems to be demonstrating a certain type of database support, not, the general use of functions. Except that many builtin functions can be used in an LV, as well as an RV context, they should be conceptually fairly straightforward to people with a programming background. The only other thing that might confuse is that dialplan actions are processed as macros, with the exception of the command, itself. i.e a text string is expanded from the result of substituting variables, functions and expressions, and that string is then used to provide the argument string that is passed to the application.

Incidentally, it is probably more common to find people confused because they don’t realise that dialplans are programs, rather than the simple configuration files you would find in typical PABXes. than people confused because they are closer assembler than object oriented. People expect to be given a pattern into which they substitute values, rather than a program.

1 Like

That kills me. I may have to learn AEL now. I wanted user defined functions because the project I’m working on was looking like it was going to be 500+ lines in my head and I wanted to shorten and make it a lot more readable. Without functions now it’s looking like 1500+ lines with goto’s jumping around everywhere. Thanks for the help.

What is a “per-defined function”?

Asterisk’s dialplan language is not a general purpose programming language so you will find it lacking features you are familiar with.

Asterisk has the “Asterisk Extension Language” (aka AEL) that has many of the flow control structures more general languages provide.

Both can call external programs written in a language of your choice that comply with the “Asterisk Gateway Interface” (AGI). I tend to use AGI a lot as I write ‘functional units’ in C, Perl, and PHP and then ‘knit’ them together with dialplan or AEL.

Asterisk also provides the “Asterisk Manager Interface” (AMI) and the “Asterisk REST interface” (ARI).

Keep in mind that Asterisk is like a box of Legos. It’s nothing until you put it together to create your vision.

Maybe if you describe what you are trying to accomplish, we could offer some useful suggestions.

Type
core show functions
on the asterisk CLI for the list.

I was not aware of that. I’ll check it out, thanks.

I assume it is a typo for pre-defined.

I’m trying to create a new modular Lenny program called Fred.
Here is the folder structure:

Greeting1
Greeting2
Greeting3
Combo1
Combo2
Filler
Confused
Mod#

The greeting folders contain different greetings that can be put together.
Example:
Greeting 1 Hello,Hi,Yes?
Greeting 2 My name is Fred, This is Fred, Fred here
Greeting 3 Whats going on?, How can I help you?, Whats up?
Confused contains confused sounded lines “can you say that again?”
Filler is “uhh”,“yea”,“ok”,“im listening”
Combo 1/2 are filler lines that can be strung together the way the greetings are.
The Mod folders are modules that contain a coherent set line of lines to play in order, that you can intersperse lines from the other folders into.

The main program would play 3 greetings, then randomly cycle through the mod folders while interjecting sound files from the combo/confused/filler folders.

Example code:

same => n,Set(greetone=${RAND(1,4)})
same => n,Set(greettwo=${RAND(1,8})
same => n,Set(greetthree=${RAND(1,8}
same => n,Playback(Fred/Greeting1/fred${greetone})
same => n,Playback(Fred/Greeting2/fred${greettwo})
same => n,Playback(Fred/Greeting3/fred${greetthree})
same => n,While($[${i} < 8]) ;6
same => n,Playback(Fred/Mod1/fred${i})
same => n,Set(i=$[${i} + 1])
same => n,EndWhile ;8
same => n,BackgroundDetect(Fred/backgroundnoise,1500)
same => n,Playback(Fred/Mod1/fred${i})  ;8
same => n,Set(i=$[${i} + 1])
same => n,BackgroundDetect(Fred/backgroundnoise,1500)
same => n,Playback(Fred/Mod1/fred${i})  ;9
same => n,Set(i=$[${i} + 1])
same => n,While($[${i} < 13])
same => n,Playback(Fred/Mod1/fred${i})
same => n,Set(i=$[${i} + 1])
same => n,EndWhile ;13

I really wanted to bundle a lot of this up into functions.

Ah yes. I never noticed until now lol.

Here’s a snippet of AEL:

                        switch  (${STEP-${IDX}-TYPE})
                                {
                                case ACCOUNT-NUMBER:
                                case LOOKUP-TTS-SNIPPETS:
                                case PROMPT:
                                case SAY-DIGITS:
                                        agi(${TOLOWER(${STEP-${IDX}-TYPE})},${DEBUG-MODE},${TEST-MODE},${VERBOSE-MODE});
                                        break;
                                case COMPLETE:
                                        if      (("760xxxxxxx" = "${CID}")
                                        ||       ("619xxxxxxx" = "${CID}")
                                        ||       ("sedwards" = "${CID}")
                                                )
                                                {
                                                playback(vtpv/ka-ching);
                                                };
                                        STATE=COMPLETE;
                                        break;
                                case DIAL:
                                        agi(dial.pl,${DEBUG-MODE},${TEST-MODE},${VERBOSE-MODE});
                                        break;
                                default:
                                        verbose(1,****** Error, unknown step type - ${STEP-${IDX}-TYPE});
                                        break;
                                };

Where:

  1. account-number asks for an account number, validates it, and sets a channel variable.
  2. lookup-tts-snippets reads rows from the database and sets channel variables.
  3. prompt plays a [list of] prompt[s].
  4. COMPLETE marks the end of the billable portion of a call.
  5. dial.pl places an outbound call to a live operator.

1, 2, and 3 are written in C. 5 is written in Perl.

1 Like

I’m googling and it looks like AEL is going to be what I need. Thanks