Lenny, The bot that fools telesales callers

lennyI mentioned in the last post that I send all but a select few callers on my landline straight to voicemail but that’s actually no longer true. Thanks to a casual comment by @ichilton on Twitter I’ve recently switched to diverting calls from unknown numbers to Lenny.

Lenny who? Lenny is an Asterisk dial plan which together with a set of very convincing recordings works as a bot to fool telesales callers into thinking they are talking to a real person. Lenny will answer then wait for the caller to speak and when they stop he moves on to the next recording, it really is very convincing and there are some great recordings of people having lengthy conversations with him on this YouTube channel.

Lenny is very simple to set up, you can find the voice files attached to this forum post and full instructions for setting it up here but briefly you just need to add the following dialplan into your /etc/asterisk/extensions_custom.conf

;# // BEGIN Lenny Remake
; version 0.3.2 copyright: none claimed. Enjoy!
exten => 53669,1,Answer
exten => 53669,n,Set(TIMEOUT(absolute)=1800)
exten => 53669,n,Set(MACHINE=0)
exten => 53669,n,Set(OPTION=5)
exten => 53669,n,Set(TALK_DETECTED=0)
exten => 53669,n,Set(RECORDING=${UNIQUEID})

exten => 53669,n,MixMonitor(/tmp/Lenny/${RECORDING}.wav)
exten => 53669,n,NoOp(Recording will be available: /tmp/Lenny/${RECORDING}.wav)
;exten => 53669,n,Playback(en/this-call-may-be-monitored-or-recorded)

exten => 53669,n,Gosub(playit(Lenny1))
exten => 53669,n,Gosub(playit(Lenny2))
exten => 53669,n,Gosub(playitonce(Lenny3))
exten => 53669,n,Gosub(playitonce(Lenny4))
exten => 53669,n,Gosub(playitonce(Lenny5))
exten => 53669,n,Gosub(playitonce(Lenny6))
exten => 53669,n,Gosub(playitonce(Lenny7))
exten => 53669,n,Gosub(playitonce(Lenny8))
exten => 53669,n,Gosub(playitonce(Lenny9))
exten => 53669,n,Gosub(playitonce(Lenny10))
exten => 53669,n,Gosub(playitonce(Lenny11))
exten => 53669,n,Gosub(playitonce(Lenny12))
exten => 53669,n,Gosub(playitonce(Lenny13))
exten => 53669,n,Gosub(playitonce(Lenny14))
exten => 53669,n,Gosub(playitonce(Lenny15))
exten => 53669,n,Gosub(playitonce(Lenny16))
exten => 53669,n,Gosub(playitonce(Lenny2))
exten => 53669,n,Gosub(playitonce(Lenny3))
exten => 53669,n,Gosub(playitonce(Lenny6))
exten => 53669,n,Gosub(playitonce(Lenny8))
exten => 53669,n,Gosub(playitonce(Lenny9))
exten => 53669,n,Gosub(playitonce(Lenny10))
exten => 53669,n,Gosub(playitonce(Lenny14))
exten => 53669,n,Playback(en/tt-monkeys)
exten => 53669,n,Hangup

exten => 53669,n(playit),NoOp(Lenny speaks and repeats until reponse)
exten => 53669,n,Set(LOCAL(lennyclip)=${ARG1})
exten => 53669,n(oncemo),Set(TALK_DETECTED=0)
exten => 53669,n,Background(lenny/${lennyclip})
exten => 53669,n,AMD(2500,1500,800,5000,100,50,3,256)
exten => 53669,n,NoOp(${AMDCAUSE})
exten => 53669,n,GotoIf($["${AMDCAUSE:0:17}"="INITIALSILENCE-25"]?reststop)
exten => 53669,n(mach),WaitForSilence(700,3)
exten => 53669,n,Goto(humn)
exten => 53669,n(reststop),WaitForSilence(800,2)
exten => 53669,n,Goto(oncemo)
exten => 53669,n(humn),Return

exten => 53669,n(playitonce),NoOp(Lenny speaks once)
exten => 53669,n,Set(LOCAL(lennyclip)=${ARG1})
exten => 53669,n(noresponse),Background(lenny/${lennyclip})
exten => 53669,n,AMD(2500,1500,800,5000,100,50,3,256)
exten => 53669,n,NoOp(${AMDCAUSE})
;exten => 53669,n,GotoIf($["${AMDCAUSE}"="INITIALSILENCE-2500-2500"]?noresponse)
;exten => 53669,n,GotoIf($[${AMDSTATUS}=HUMAN]?humn2:mach2)
exten => 53669,n(mach2),WaitForSilence(2000,1)
exten => 53669,n(humn2),Return

exten => 536691,1,NoOp(Rerouting inbound call...)
;exten => 536691,n,Flite("After the beep, enter extension or press pound for Lenny.")
;exten => 536691,n,Read(SENDTO,beep,7)
;exten => 536691,n,GotoIf($["foo${SENDTO}" = "foo"]?5:6)
exten => 536691,n,Set(SENDTO=53669)
exten => 536691,n,System(echo "Channel: local/${SENDTO}@from-internal" > /tmp/lenny.call)
exten => 536691,n,System(echo "MaxRetries: 0" >> /tmp/lenny.call)
exten => 536691,n,System(echo "RetryTime: 3" >> /tmp/lenny.call)
exten => 536691,n,System(echo "WaitTime: 30" >> /tmp/lenny.call)
exten => 536691,n,System(echo "Context: bridgit" >> /tmp/lenny.call)
exten => 536691,n,System(echo "Extension: 4" >> /tmp/lenny.call)
exten => 536691,n,System(echo "Priority: 1" >> /tmp/lenny.call)
exten => 536691,n,System(mv /tmp/lenny.call /var/spool/asterisk/outgoing)
;# // END Lenny Remake

Copy the audio files to /var/lib/asterisk/sounds/lenny and make sure the directory and contents are owned by the asterisk user & group.

Reload the Asterisk dialplan with: asterisk -rx “dialplan reload”

and then you can check it is working by dialling extension 53669.

You can then divert any unwanted calls to this extension to have some fun. I did this by creating a custom extension in FreePBX with a destination of local/53669@from-internal and making this the default route for unknown numbers coming in via the PSTN trunk.

To get call recordings emailed to yourself you can use the following shell script called from cron, you’ll want to make sure you have lame and the mime-construct packages installed.

subj="Lenny Recording $(date)"

cd /tmp/Lenny
for i in *.wav; do
if [ -e "$i" ]; then
file=`basename "$i" .wav`
lame -h -b 64 "$i" "$file.mp3"
rm "$file.wav"
/usr/bin/mime-construct --file-auto "$file.mp3" --to $email --subject "$subj"
rm "$file.mp3"

So far I haven’t recorded anything of interest, it seems most the telesales calls I have been getting are pre-recorded messages but as soon as I get something good I’ll upload it here.


9 thoughts on “Lenny, The bot that fools telesales callers

  1. Had my first real call, most seem to be other computers calling and hanging up. Anyway, the email was sent to my gmail account – however was renamed to “noname” and possibly was truncated. I’ve edited your script to –file-attach “$file.mp3” – it now retains the name and file type. Hope “Microsoft” call again.

  2. My only issue is it not sending the recordings to the directory it says

    this is the line of code that is suposed to send the recording to the dir

    exten => 53669,n,NoOp(Recording will be available: /tmp/lenny/${RECORDING}.wav)

    But i have checked that dir and nothing is in it after a test call to the lenny system.

  3. “You can then divert any unwanted calls to this extension to have some fun. I did this by creating a custom extension in FreePBX with a destination of local/53669@from-internal and making this the default route for unknown numbers coming in via the PSTN trunk.”

    Can you explain this better? I’m just moving from Incredible PBX to FreePBX and I couldn’t recover my setup. I can call internally, but the setup from OBI to Pi is a grey area. It rings and rings, and the Set Destination isn’t showing the Custom Extension (via admin).

  4. Just to add, I can also call through and create an inbound route to “Feature Code Admin: Speaking Clock “, which works. But as mentioned, I can’t get to the “Admin > Custom Extension > 53669” that I created. Calling 53669 from a connected soft phone goes straight to Lenny.


  5. In Inbound Routes I created a route for the incoming line from my SPA3000 with the DID set as my landline number and the CallerID blank and the destination as the Lenny extension. This is the default route for calls.

    eg. https://nathan.chantrell.net/downloads/asterisk/lennyraspbx1.png

    Then for the half dozen or so people who might ever legitimately call me on the landline I created separate routes with the DID as the landline number and the CallerID as their number with the destination to my extension.

    eg. https://nathan.chantrell.net/downloads/asterisk/lennyraspbx2.png

    Although at this point I could probably safely send everything to Lenny, even my mum has finally given up calling the landline now!

Leave a Reply

Your email address will not be published. Required fields are marked *

Notify me of followup comments via e-mail. You can also subscribe without commenting.