Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save hgaibor/c8d65e5b6c56ffc7b7f1d73e127641aa to your computer and use it in GitHub Desktop.
Save hgaibor/c8d65e5b6c56ffc7b7f1d73e127641aa to your computer and use it in GitHub Desktop.
targeted-spy-with-cos-and-exts-restrictions
; Feature code + Class of Service implementation to allow certain users to spy/whisper/barge on
; a specified extension.
; NEW: Ability to set the list of extensions that can be spied for each extension.
; This will use the accountcode variable inside of the extension --> [advanced] tab
; Extensions list will be separated by & (eg: 100&200&201...) MAX limit 255 chars!!
;
; Latest version:
; https://gist.github.com/hgaibor/
;
; Forked from:
; https://gist.github.com/hgaibor/82433116540f15ab345b2914500abb0d
; https://gist.github.com/hgaibor/6a170c150cf9dec607620aa44d78fcd0
;
; Usage and install instructions:
; 1. On FreePBX/PBXact GUI, Create a Custom Destination that points to the [custom-spy-with-cos-and-restrictions] context.
; Fill the interface fields with the values after the --> :
; 1.1 Target --> custom-spy-with-cos-and-restrictions,s,1
; 1.2 Description --> Put a brief description
; 1.3 Notes --> Put your detailed comments here
; 1.4 Return --> no
;
; 2. Create a Misc Application that will point to created custom destination above.
; This destination will have the feature code you choose and will allow you to set the permissions over the
; Class of Service module.
; 2.1 Enable --> Yes
; 2.2 Description --> Spy with permissions feature code
; 2.3 Feature Code --> *557 (you can change this to whatever code needed)
; 2.4 Destination --> SELECT Custom Destination created above
;
; 3. Create Class of Service rules to DENY all users to use this code and allow the ones you need.
; Since you used the Misc Application module to assign the feature code, it will appear under the
; Class of Service --> [Feature codes] tab
;
; 4. Put Custom Dialplan at (GUI can be used using Config edit module):
; /etc/asterisk/extensions_custom.conf
; Dial feature code assigned under Misc Application to start in spy mode. While spying on
; active channel use the following dtmf input to toggle modes:
; dtmf 4 - spy mode
; 5 - whisper mode
; 6 - barge mode
;
; 5. Set the list of extensions that can be spied by the current extension, in the 'accountcode' field at the [advanced] tab.
; format wil be the following: '100&101&200&201&2000' and so on.. separate extensions with &
; extensions will be able to spy every other extension that is listed under its accountcode,
; unless they are prevented from using the feature code by the Class of service module permissions.
;
; This is default behavior mentioned in https://wiki.asterisk.org/wiki/display/AST/Application_ChanSpy
;
; License: GNU GPL2
;
; Version History
; 2023-08-07 First public commit by hgaibor
; For testing spy level restrictions without COS, if you don't want to perform steps 1 to 3 mentioned above uncomment this
; block of code and use the code combination of your preference
;[from-internal-custom]
;exten => *557,1,Noop(Entering user defined context custom-spy-with-cos-and-restrictions in extensions_custom.conf)
;exten => *557,n,GoTo(custom-spy-with-cos-and-restrictions,s,1)
[custom-spy-with-cos-and-restrictions]
exten => s,1,Noop(Entering user defined context custom-spy-with-cos-and-restrictions in extensions_custom.conf)
; Get info on the originating extension
exten => s,n,Set(__SPY_EXT=${CALLERID(num)}) ; numeric spy level permission, will allow spy if this is greated than the SPIED_EXT accountcode value
exten => s,n,noop(Dialer Spy ext: ${SPY_EXT})
; Get info on the extension that will be spied
exten => s,n,Read(__SPIED_EXT,please-enter-the&extension&number&followed_pound)
exten => s,n,noop(Spied ext: ${SPIED_EXT})
exten => s,n,GoTo(check-spy-restrictions,s,1)
[check-spy-restrictions]
; This context will use the value set up in extensions Account Code in order to determine the extensions this user can spy
; It will allow spying on extensions listed at current extensions accountcode field.
; NOTE, Account code MAX lenght is 255 chars, keep this in mind, as it will count the & signs as part of this lenght
exten => s,1,Noop(Entering user defined context check-spy-restrictions in extensions_custom.conf)
exten => s,n,Set(SPY_EXTS=${DB(AMPUSER/${SPY_EXT}/accountcode)}) ; Checking SPY EXTS, separated by & sign. Will allow spy if target extension is present on this list
exten => s,n,Set(SPY_EXTS=${STRREPLACE(SPY_EXTS,&,\,)}) ; comma delimited list of extensions
exten => s,n,ExecIf($[!${LEN(${SPY_EXTS})}]?Set(SPY_EXTS=0))
exten => s,n,noop(Dialer allowed spy extensions: ${SPY_EXTS})
; step thru each extension in the list and compare if it matches spied extension
exten => s,n,While($["${SET(SPY_EXT=${POP(SPY_EXTS)})}" != ""])
exten => s,n,noop(SPY_EXT: ${SPY_EXT})
exten => s,n,GotoIf($["${SPY_EXT}"="${SPIED_EXT}"]?AllowSpy) ; if an active channel is found exit the loop
exten => s,n,EndWhile()
exten => s,n,GoTo(DenySpy)
exten => s,n,Hangup()
exten => s,n(AllowSpy),noop(Allowed spy from ext ${SPY_EXT} to ${SPIED_EXT})
exten => s,n,GoTo(targeted-chanspy,${SPIED_EXT},1)
exten => s,n,Hangup()
exten => s,n(DenySpy),noop(DENIED! spy from ext ${SPY_EXT} to ${SPIED_EXT})
exten => s,n,Playback(silence/1&sorry-cant-let-you-do-that&silence/1)
exten => s,n,Hangup()
exten => h,n,Hangup()
exten => s,n,Hangup()
exten => T,n,Hangup() ; timeout destination
[targeted-chanspy]
exten => _.,1,Noop(Entering user defined context targeted-chanspy in extensions_custom.conf)
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,n,Hangup()
exten => s,n,Hangup()
exten => T,n,Hangup() ; timeout destination
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment