Answers‎ > ‎

Meet Lenny, telemarketers' and annoying callers' endgame

posted Aug 28, 2015, 6:17 PM by EZvoip Co   [ updated Sep 9, 2015, 7:54 AM ]
Lenny is a set of prerecorded voice messages that kick in sequentially whenever there is silence on the other end, creating the appearance of a conversation, designed to waste the caller's time. We can send certain callers to Lenny based on Caller ID or failure to perform a simple test, such as pressing a digit on the keypad within the allotted time as part of an IVR.

If you have an account with us, you may also transfer an annoying caller straight to Lenny. You could say, for instance, Please hold, I'll transfer you to Lenny, our "vice-president" (or, if it's a residential number, "the head of our household").

Here's Lenny talking to a Vonage telemarketer:

YouTube Video - Recording of Vonage Telemarketer


Here's Lenny talking to a Wildrose Party telemarketer:

YouTube Video: Wildrose party telemarketer



Here's Lenny talking to a Progressive-Conservative (PC) Party person campaign volunteer soliciting donations (she's actually very patient!):

YouTube Video: PC Party / Polievre


The last recording, above, was covered in the Canadian press:
<<Nice fellow, that Lenny, though as a campaign worker for Pierre Poilievre discovered recently, you don’t want to get stuck talking to him.
“Oh good. Yes … yes, yes,” answers Lenny when the worker, whose name sounds something like “Segalle,” asks if he’s willing to put an election sign on his lawn.
But as a recording of the 11-minute, 14-second conversation posted to YouTube reveals, it’s all befuddlement from there.
“What was that again?” Lenny asks in his soft British accent. “Sorry, again?”
Segalle obligingly raises her voice as she quizzes Lenny on the voting intentions of his household, only to be treated to inside information on Lenny’s daughters — Rachel, a bit of a hothead, you know, and Larissa, the third-eldest but first in the family to go to university — before he again asks her why she’s calling.
And then there are the ducks. “Sorry, could you just hang on for one second here, hang on,” says Lenny to a chorus of quacks in the background.
Lenny, as you’ll have gathered by now, though poor, patient Segalle apparently never did, is a recording — a series of pitch-perfect responses designed to fool callers into thinking they’ve reached a genuine person with genuine interest in whatever they’re pitching.>>

Technical Details

Here's Lenny's DNA (FreeSwitch LUA Code, also at http://pastebin.com/ujUzxsxz):
  1. --[[
  2. It's Lenny in Lua
  3. --]]
  4.  
  5. local path = '/usr/local/freeswitch/sounds/lenny/';
  6. local counter_mainloop = 1;
  7. local counter_noinput  = 1;
  8. local counter_consec_noinput = 0;
  9.  
  10. function get_filename (aType, aNumber)
  11.    local file = path .. aType .. '/' .. aNumber .. '.raw';
  12.    local fd = io.open(file, "r");
  13.    if fd == nil then
  14.       file = path .. aType .. '/' .. aNumber .. '.gsm';
  15.    end
  16.    io.close(fd);
  17.    return file;
  18. end
  19.  
  20. function wait_for_silence(aTimeoutIncrement)
  21.    if aTimeoutIncrement == nil then
  22.       aTimeoutIncrement = 2000;
  23.    end
  24.    freeswitch.consoleLog( 'DEBUG', "ENTER wait_for_silence " .. aTimeoutIncrement .. "\n" );
  25.    if session:ready() ~= true then
  26.       return false;
  27.    end
  28.    session:setVariable("wait_for_silence_timeout" , "");
  29.    session:setVariable("wait_for_silence_listenhits", "0");
  30.    session:setVariable("wait_for_silence_silence_hits", "0" );
  31.    session:execute( "wait_for_silence", "300 30 5 " .. aTimeoutIncrement);
  32.  
  33.    local timeout = tonumber(session:getVariable("wait_for_silence_timeout"));
  34.    local speech  = tonumber(session:getVariable("wait_for_silence_listenhits"));
  35.    local silence = tonumber(session:getVariable("wait_for_silence_silence_hits"));
  36.  
  37.    freeswitch.consoleLog( 'DEBUG', "Speech : " .. speech .. " Silence : " .. silence .. "\n" );
  38.    if speech > 20 then
  39.       wait_for_silence( aTimeoutIncrement );
  40.       return true;
  41.    else
  42.       return false;
  43.    end
  44. end
  45.  
  46. function play_next_mainloop ()
  47.    session:execute( "playback", get_filename( 'mainloop', counter_mainloop ) );
  48.    counter_mainloop = counter_mainloop + 1;
  49.    local fd = io.open( get_filename( 'mainloop', counter_mainloop ) , "r");
  50.    if fd == nil then
  51.       counter_mainloop = 1;
  52.    end
  53.    counter_consec_noinput = 0;
  54.    io.close(fd);
  55.    return 2000;
  56. end
  57.  
  58. function play_next_noinput ()
  59.    session:execute("playback", get_filename( 'mainloop', counter_mainloop ) );
  60.    counter_noinput = counter_noinput + 1;
  61.    counter_consec_noinput = counter_consec_noinput + 1;
  62.    local fd = io.open( get_filename( 'noinputloop', counter_noinput ) , "r");
  63.    if fd ~= nil then
  64.       counter_noinput = 1;
  65.    end
  66.    if counter_consec_noinput > 3 then
  67.       counter_mainloop = 1;
  68.    end
  69.    io.close(fd);
  70.    return 3200;
  71. end
  72.  
  73. session:answer();
  74. session:execute( "playback", get_filename( 'greeting', 1 ) );
  75. local SilenceTimeout = 4000;
  76. while session:ready() == true do
  77.    if wait_for_silence( SilenceTimeout ) ~= true then
  78.       SilenceTimeout = play_next_noinput();
  79.    else
  80.       SilenceTimeout = play_next_mainloop();
  81.    end
  82. end

A loop code was published:
[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)
..together with a ZIP archive.

Make Lenny part of your IVR, as heard and discussed; also, install.
Comments