Skip to content

Instantly share code, notes, and snippets.

@lgaetz
Last active October 9, 2025 12:36
Show Gist options
  • Select an option

  • Save lgaetz/78c4e114952e79596c1ed4123559d3d3 to your computer and use it in GitHub Desktop.

Select an option

Save lgaetz/78c4e114952e79596c1ed4123559d3d3 to your computer and use it in GitHub Desktop.
FreePBX Feature code prefix to allow spy/whisper/barge on the specified extension
; FreePBX Feature code prefix to allow spy/whisper/barge on
; the specified extension.
;
; Latest version:
; https://gist.github.com/lgaetz/78c4e114952e79596c1ed4123559d3d3
;
; Usage:
; Dialplan goes in the file:
; /etc/asterisk/extensions_custom.conf
; Dial local extension with 556 prefix to spy. While spying on
; active channel use the following dtmf input to toggle modes:
; dtmf 4 - spy mode
; 5 - whisper mode
; 6 - barge mode
;
; License: GNU GPL2
;
; Version History
; 2018-11-23 First commit by lgaetz
; 2019-03-05 update regex to prevent channels from returning multiple channel matches
; 2019-10-10 added support to loop thru all dialable devices for the extension, now supports Zulu/webrtc clients
; 2020-04-12 COVID-19 edition - supports prompt to request spyee extension
; 2020-07-23 add E option to ChanSpy application and add timeout
; 2021-03-08 add example line to block spying access to certain extensions
[from-internal-custom]
exten => 556,1,Noop(Entering user defined context from-internal-custom in extensions_custom.conf)
exten => 556,n,Read(spyee,please-enter-the&extension&number&followed_pound)
exten => 556,n,GoTo(targeted-chanspy,${spyee},1)
exten => _556.,1,Noop(Entering user defined context from-internal-custom in extensions_custom.conf)
exten => _556.,n,GoTo(targeted-chanspy,${EXTEN:3},1) ;strip off prefix
[targeted-chanspy]
exten => _.,1,Noop(Entering user defined context targeted-chanspy in extensions_custom.conf)
; exten => _.,n,ExecIF($["${EXTEN}"="100"]?Hangup) ; if you want to block spying access to certain extensions you can do that with this line, one per ext
exten => _.,n,Set(TIMEOUT(absolute)=3600) ; prevent hung channel by setting 1 hour timeout recommended if using infinite loop
exten => _.,n,Answer
exten => _.,n(once-upon-a-time),Wait(1)
exten => _.,n,set(spy_target=) ; initialize target var
; get list of dialable devices for extension
exten => _.,n,Set(DEVS=${DB(AMPUSER/${EXTEN}/device)}) ; & delimited list of devices
exten => _.,n,Set(DEVS=${STRREPLACE(DEVS,&,\,)}) ; comma delimited list of devices
; step thru each device and look for an active channel
exten => _.,n,While($["${SET(DEV=${POP(DEVS)})}" != ""])
exten => _.,n,noop(dev: ${DEV})
; using a regex of SIP/${EXTEN}- will match both SIP and PJSIP channels, and the trailing - character should
; help to ensure there is only a single match. If multiple channels are returned the chanspy application will fail
exten => _.,n,set(spy_target=${CHANNELS(SIP/${DEV}-)})
exten => _.,n,ExecIf($["${spy_target}"!=""]?ExitWhile) ; if an active channel is found exit the loop
exten => _.,n,EndWhile()
exten => _.,n,ExecIF($["${spy_target}"!=""]?ChanSpy(${spy_target},dnqE)) ; q option suppresses channel announce on barge
exten => _.,n,Hangup() ;comment this line with a semicolon to do infinite loop
exten => _.,n,GoTo(once-upon-a-time)
exten => _.,n,Hangup()
exten => h,1,Hangup()
exten => s,1,Hangup()
exten => T,1,Hangup() ; timeout destination
@lgaetz
Copy link
Author

lgaetz commented Nov 9, 2022

Couple ways that could be done. Suggest you open a thread on the FreePBX forum for ideas. https://community.freepbx.org/

Copy link

ghost commented Jun 25, 2023

This is absolutely brilliant, works perfectly and I hope one day this is included in FreePBX.

For anyone who uses this in the UK, edit "followed_pound" to "astcc-followed-by-the-hash-key" for the UK voice, otherwise it speaks the UK language for the first part of the instructions, then the US one for the hash key part.

@XcamaroX
Copy link

@lgaetz Great code, it works as intended. Thank you so much for your hard work on this.

One quick question, this only seems to work when the extensions are on the same PBX, but how would it work if I have 2 PBX's, connected via a PJSIP trunk?

When I try to barge on the remote extensions, it just hangs up.

The exteniosn are OK, as I can make calls to and from and it works.

Would this code work for this instance, any modifications that I might have to do?

Thank you again!

@clletizia
Copy link

Greetings and thanks for the code. Is it expected that the exclusion statement < ExecIF($["${EXTEN}"="100"]?Hangup) > will handle traditional dialplan wild cards? for instance ExecIF($["${EXTEN}"="1xx"]?Hangup). Thanks

@lgaetz
Copy link
Author

lgaetz commented Nov 29, 2023

exclusion statement ... will handle traditional dialplan wild cards?

No. To do that you'll want to investigate the REGEX dialplan function.

@clletizia
Copy link

clletizia commented Nov 29, 2023 via email

@ariefly
Copy link

ariefly commented Oct 8, 2025

i have extension from 1-10000 so if the extension 10000 use this feature so i have to change to ${EXTEN:5}, is that right ?

@lgaetz
Copy link
Author

lgaetz commented Oct 9, 2025

Probably not. If you need detailed help drop by the TangoPBX forum and share your dialplan
https://community.tangopbx.org/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment