Learning Astrisk, can we breakdown the lenny script?

Hi, I’m trying to understand how the lenny script works.
I understand it works because the files are named Lenny1-16 and the script iterates by changing

[Lenny]

exten => talk,1,Set(i=${IF($["0${i}"="016"]?7:$[0${i}+1])})

same => n,ExecIf($[${i}=1]?MixMonitor(${UNIQUEID}.wav))

same => n,Playback(Lenny/Lenny${i})

same => n,BackgroundDetect(Lenny/backgroundnoise,1500)

The first line:
exten => talk,1,Set(i=${IF($["0${i}"="016"]?7:$[0${i}+1])})
exten gets the current context, not sure what does => but it seems to be default syntax.
I can’t find anything about what talk is, I found TALK_DETECT in the manual but I don’t think that’s what it is.

Set(i=${IF($["0${i}"="016"]?7:$[0${i}+1])})
This is setting the value of i but I have no idea whats happening here, looks like regex.
I also can’t find what the $ does anywhere (google is no help)

Line 2:
same => n,ExecIf($[${i}=1]?MixMonitor(${UNIQUEID}.wav))
I think this line is making sure Lenny1 is not played again and somehow mixing lenny clips to create new sounds?
n represents numbers 2-9 and ExecIf executes an Asterisk application under specific conditions. This is getting a little clearer.

MixMonitor records a call and mix the audio during the recording. UNIQUEID is as the name says. I’m guessing this somehow records clips of lenny as it plays and mixes them together and gives them a unique numbers?

The third line:
same => n,Playback(Lenny/Lenny${i})
This clearly plays the sound file Lennyi were i = current iteration
I don’t understand why n is there.

The forth line:
same => n,BackgroundDetect(Lenny/backgroundnoise,1500)
This is listening for sound to start and end before playing another lenny clip.

If anyone could explain the first 2 lines of the script I would really appreciate it, as you can see I’m very lost.

The first few questions are foundation level Asterisk. Please take a look at asterisk/extensions.conf.sample at master · asterisk/asterisk · GitHub which should be included with your Asterisk, for basic explanations, and some not so basic ones, of Asterisk dialplans.

Also, although somewhat dates, Dialplan Syntax still covers the basics that you seem to be missing.

“talk” is the name of an extension, and I stopped reading at the point where you didn’t know this as it is just too basic a question to get stuck on.

If this is not the place to ask basic questions where can one go to ask basic questions?

User the preformatted text button when editing your posts to make code readable

Familiarize yourself with wiki.asterisk.org

Very quickly:

  1. increments a channel variable named i by one each time the line runs until it reaches 16 after which i stays at fixed value
  2. Checks the value of i and enables recording on the channel if i == 1
  3. Plays the sound file corresponding to the value of i
  4. Plays sound file backgroundnoise while listening for dtmf input from caller

I presume there is more

I looked at the wiki but all the information is incoherently scattered all over the place.
The Getting started, Beginning Asterisk page just throws random 15+ year old links at you and warns you that some of the information might be incorrect.

The hello world example uses

allow=ulaw
auth=6001
aors=6001

Syntax,
Then immediately we go to
astetcdir => /etc/asterisk
with no explanation of what the => does.
Is it a lambda operator, syntactic sugar, a special operator for dealing with files?
It doesn’t say.

Only much later under
Fundamentals/Asterisk Configuration/Asterisk Configuration Files/Config File Format/Objects
does it say that

In order to make life easier for newcomers to the Asterisk configuration files, the developers have made it so that you can also create objects with an equal sign. Thus, the two lines below are functionally equivalent.

some_object => settings
some_object=settings

It is common to see both versions of the syntax, especially in online Asterisk documentation and examples. This book, however, will denote objects by using the arrow instead of the equals sign.

So before when we looked at astetcdir => /etc/asterisk
astetcdir is an object inheriting from /etc/asterisk?

The text underneath it said

This location is used to store and read Asterisk configuration files. That is generally files with a .conf extension, but other configuration types as well

I just want a clear breakdown of how this stuff works. The wiki does not provide that.

1 Like

I am having trouble understanding this script too. So, I re-wrote a cleaner version of it. The original was such terrible shorthand script, the new one has an explicit while loop and for loop. Much better.
(DID numbers are redacted)

[from-pstn]
exten => _+1******0155,1,Answer(500)
same => 1,Log(DEBUG,Calling in 0155 ${EXTEN})
same => n,Answer
same => n,MixMonitor(${UNIQUEID}.wav)
same => n,Set(i=1)
same => n,While($[${i} < 17])
same => n,Playback(Lenny/Lenny${i})
same => n,BackgroundDetect(Lenny/backgroundnoise,1500)
same => n,Set(i=$[${i} + 1])
same => n,EndWhile
same => n,Set(i=${RAND(5,16)})
same => n,While($[${i} < 17])
same => n,Playback(Lenny/Lenny${i})
same => n,BackgroundDetect(Lenny/backgroundnoise,1500)
same => n,Set(i=${RAND(2,15)})
same => n,EndWhile
same => n,Hangup
2 Likes

Wow that is so much cleaner.
Thanks.

This line,

same => n,MixMonitor(${UNIQUEID}.wav)

Does this just record the call and save it as a .wav file?
Looking at the original code I had no idea how this was being used but here it seems pretty clear.

Yes, the MixMonitor will record your calls to /var/spool/asterisk/monitor as a .WAV file.
By the way, replace RAND(2,15) with RAND(5,16) in the 3rd to last line. This will draw
a random number to play a random message from the lenny scripts. You should not play
the first 4 after the first loop, or lenny will sound like he’s really dumb.

Thanks, I’m working on a new program like lenny with a different voice file set.
I was looking for ways to add more control to the conversation and this is perfect.

I would like to have some more recordings of Lenny and maybe also Larissa, his 3rd eldest daughter.

I can explain the 4 lines:
line 1: starts a number counting loop to 16 with variable i, second time around, play from 7
line 2: starts the call recording, if i=1
line 3: in the loop, play a Lenny recording numbered with i from the set of 16
line 4: pause, wait 1.5 seconds, while detecting the other caller’s voice, plus play the backgroundnoise recording, which is mostly silence.
This script does not really work. In practice, it will just play the first recording, then drop the call.

What I have found so far, is that people will interact with the first round, but on the second round with random Lenny selection, they start to realize that Lenny is nuts, and kindly wish him all the best and hang up.

Thanks. I’m a C programmer so I understand what it does from a programmers perspective but the syntax and asterisk specific stuff kills me because of how crunched it is and I’m struggling to find good reference material.
If i can write while loops like your example I’m going with that.

If I wanted to play multiple audio files in a row, with say a pause in between but not have it wait for the person on the other end to say something would something like this work assuming i = 5 at the start of this loop?

same => n,While($[${i} < 8])
same => n,Playback(Lenny/Lenny${i}) ;first line
same => n,Set(i=$[${i} + 1])
same => n,Playback(Lenny/Lenny${i}) ;this would be a 3s silent audio file
same => n,Set(i=$[${i} + 1])
same => n,Playback(Lenny/Lenny${i}) ;second line
same => n,Set(i=$[${i} + 1])
same => n,EndWhile
another loop starting with
same => n,BackgroundDetect(Lenny/backgroundnoise,1500)

Do you know a better way of doing this?

<…> in the following is a meta name.

exten => <name>,1,<command>

defines the first step (called priority in Asterisk) of the processing for extension <name> in the current context. I consider it a reasonable expectation that anyone asking help here already knows this.

Set(<variable>=<string>)

Is a command that set a variable to a literal string.

$ is a general prefix for macro processing operators.

${<variable>} macro substitutes the values of that variable, as a string, and ${<function>(<parameters>), runs that, built-in, function with the string literal parameters, and macro substitutes the result. Again people are expected to know this.

${IF(<condition>?<truevalue>:<falsevalue>) is a function that returns truevalue or false value depending on whether condition is 1 or 0.

$[<expression>] macro substitutes the result of evaluating the expression, as the string representation of the value of the expression.

= is a comparison operator, which does numeric comparisons on pure numbers and otherwise string ones. It gives 1 for true and 0 for false.

" here just acts as part of the value and makes it a string, rather than a number.

+ is the arithmetic addition operator.

The next step would be:

exten => <name>,2,<command2>

but:

same => is a shorthand for exten => <name> with <name> the same as that actually used on previous line.

n is a shorthand tor the result of adding one to the priority used on the previous line.

ExecIf(<condition>?<command>) is a command that executes the provided commend if the condition is true.

${UNIQUEID} is a shorthand for the read only function call ${CHANNEL{uniqueid)}. (The variable substitution code recognizes certain variable names as special and treats them like functions. More and more of these have corresponding functions.)

${CHANNEL(<property-name>)} is a function that returns certain properties of the current channel.

The uniqueid property of a channel is a string that should be globally unique, including time, that identifies a particular instance of the data structure that represents the channel.

MixMonitor(<filename>) is a command that starts the, asynchronous, recording of a mixed copy of both incoming and outgoing media on the current channel to the named file. .wav in the file name tells Asterisk to record in RIFF wrapped (Windows WAV), 16 bit, 8kHz, mono, signed linear format.

Playback(<filename-prefix>) is a command that looks for a file with the given prefix and the best available extension known to Asterisk for conversion to the codec currently in use.

BackgroundDetect(<parameters>) is a new one on me, and I would have to go to the documentation as well.

Preparing this reply took about one and a half orders of magnitude more time that most people would spend on a reply, which is one reason why it wasn’t reasonable to get a reply. Generally you are expected to know the basics and be looking for one small missing detail.

Note that I have only described the forms actually used in the example, so this is not complete documentation.

1 Like

Me too :slight_smile:

You may want to look at AEL. It’s a bit more of a ‘real’ programming language.

Here’s a snippet:

// start
                while   (${MAX-STEP} >= ${IDX})
                        {
                        verbose(1,IDX = ${IDX}, RECORDING-IDX = ${RECORDING-IDX});
                        system(logger -i -t dialplan ${CLIENT-ID}-${CALL-ID} ${CONTEXT} ${IDX} ${RECORDING-IDX} ${STEP-${IDX}-TYPE});
                        if      ("YES" != "${STEP-${IDX}-ACTIVE}")
                                {
                                set(IDX=00$[ ${IDX} + 1 ]);
                                IDX=${IDX:-3};
                                set(RECORDING-IDX=00$[ ${RECORDING-IDX} + 1 ]);
                                RECORDING-IDX=${RECORDING-IDX:-3};
                                };
                        if      ("END" = "${STEP-${IDX}-TYPE}")
                                {
                                break;
                                };
                        verbose(1,${STEP-${IDX}-TYPE});
                        switch  (${STEP-${IDX}-TYPE})
                                {
                                case BRANCH:
                                        agi(branch,${DEBUG-MODE},${TEST-MODE},${VERBOSE-MODE});
                                        break;
                                case COMPLETE:
                                        if      (("760xxxxxxx" = "${CID}")
                                        ||       ("619xxxxxxx" = "${CID}")
                                        ||       ("619xxxxxxx" = "${CID}")
                                        ||       ("619xxxxxxx" = "${CID}")
                                        ||       ("sedwards" = "${CID}")
                                                )
                                                {
                                                playback(ka-ching);
                                                };
                                        STATE=COMPLETE;
                                        break;
                                        .
                                        .
                                        .

The biggest ‘drawback’ is that a missing semi-colon can cause inscrutable errors – but at least (unlike dialplan) you do get some error messages.

Thank you!
This should be a page in the wiki.
For the life of my I have not been able to find a comprehensive guide for the syntax of the dialplan.
For example,

n is a shorthand tor the result of adding one to the priority used on the previous line.

Try using google to find that without any prior knowledge. Just searching for asterisk n, pbx n, or searching the wiki for n gives you horrible results or points you to the wildcard N that matches 2-9. So I’m left thinking every use of n is a random number between 2-9 as I’m trying to make sense of the code.

That’s interesting. I might try that out later. Thanks for the info.
Is there an IDE or debugger for this stuff?
I’m always very hesitant to use trial and error to learn on my pbx.

Googling ‘asterisk dialplan same’ yields:

  1. Contexts, Extensions, and Priorities
  2. Dialplan Syntax
  3. Asterisk priorities (obsolete, but may yield insight.)

Nope. You can debug an AGI, but you can’t debug the dialplan.