User Rating: 5 / 5

Star Active Star Active Star Active Star Active Star Active
 
dialing-the-phone.jpg

This could be wrong or could be right. I do not know, but what I have seen is that since the COVID-19 pandemic started, businesses have been struggling with how to get the income. Sadly, some indispensable businesses know their status and they have started to impose new fees. The telecommunication industry is no exception; since November 2020 at some point, carriers started to apply a short call policy with a fee. If the policy already existed and not enforced, I can't tell, but what I can is the charge. If you ask one of them why the charge, they will answer they try to stop dialers. Again, nobody can tell if this is the real reason.

Possible Scenarios

Fortunately, a short call definition for everybody is any answered call that is six or fewer seconds in length. When a call is terminated (outgoing call made), if the callee answers and the call is hung up before 6 seconds, the fee applies. There are two possible scenarios:

  1. the call is finished by the callee and
  2. the call is finished by the caller.

When the call is finished by the callee, there is not too much to do. Usually, this happens when a human answers and it is a wrong number, or a recording starts (diallers). Nothing we can do about this.

However, when the caller ends the call, we can still do something to force the outgoing call to be longer than six seconds. Take the following common scenario: you are trying to call your teenager, like any teenager, they won't answer the phone even if they have it in their hands. Instead, the call is routed to a voicemail. You hang up right away and you have a short call.

caller callee

My Solution: Force Calls to Be Longer than Six Seconds

Because the other end of the call is a voice mail (an IVR or auto-attendant), the only possible way this call can be finished is by the caller. So, what if we force the call to continue without the caller to be present. This way, this call would be more than six seconds.

moh callee

For FreeSWITCH or FusionPBX, follow these steps:

  1. Create a local dial plan that plays a sound. You can use the music on hold dial plan from FusionPBX (*9664). Clone it and make these changes:
    • change the destination_number to a new *code (something free). I usually use letters for this as this dial plan is not meant to be called by an endpoint directly. I will use the *long in this example.
    • before any pre_answer or answer action, insert a sched_hangup +10 NORMAL_CLEARING action.
  2. Edit your current outgoing route and add the following:
    • export nolocal:exec_after_bridge_app=transfer and export nolocal:exec_after_bridge_arg=*long

Dialplans look as follow:

<extension name="long" continue="false" uuid="916473cf-68e3-43d2-afeb-63e5fa86653d">
<condition field="destination_number" expression="^\*long$"/>
<condition field="${sip_has_crypto}" expression="^(AES_CM_128_HMAC_SHA1_32|AES_CM_128_HMAC_SHA1_80)$">
<action application="sched_hangup" data="+10 NORMAL_CLEARING"/>
<action application="answer" data=""/>
<action application="execute_extension" data="is_secure XML ${context}"/>
<action application="playback" data="${hold_music}"/>
<anti-action application="sched_hangup" data="+10 NORMAL_CLEARING"/>
<anti-action application="set" data="zrtp_secure_media=true"/>
<anti-action application="answer" data=""/> <anti-action application="playback" data="silence_stream://2000"/>
<anti-action application="execute_extension" data="is_zrtp_secure XML ${context}"/>
<anti-action application="playback" data="${hold_music}"/></condition>
</extension>

<extension name="lcr.1d10" continue="false" uuid="f6b2ee23-a656-4c13-9765-4502946d3d5c">
<condition field="destination_number" expression="^\+?1?(\d{10})$">
<action application="set" data="sip_h_X-accountcode=${accountcode}"/>
<action application="set" data="call_direction=outbound"/>
<action application="set" data="hangup_after_bridge=true"/>
<action application="set" data="effective_caller_id_name=${outbound_caller_id_name}"/>
<action application="set" data="effective_caller_id_number=${outbound_caller_id_number}" inline="true"/>
<action application="set" data="inherit_codec=true"/>
<action application="set" data="ignore_display_updates=true"/>
<action application="set" data="callee_id_number=$1"/>
<action application="set" data="continue_on_fail=true"/>
<action application="export" data="nolocal:exec_after_bridge_app=transfer"/>
<action application="export" data="nolocal:exec_after_bridge_arg=*long"/>
<action application="bridge" data="lcr/default/1$1"/>
</condition>
</extension>

Side-Effects?

So far, I have found that you may have two calls in your CDR records. One as an outgoing call from your extension to the external number, and a second one as an outgoing call from the external number to the music extension (*long in my example).

Workaround

Happily, for us, there is a workaround to this little side-effect. Using my Enhanced XML Importer and activating the cdr_delete plugin you will be able to delete the orphan record. Your dial-plan would look like this:

<extension name="long" continue="false" uuid="916473cf-68e3-43d2-afeb-63e5fa86653d">
<condition field="destination_number" expression="^\*long$"/>
<condition field="${sip_has_crypto}" expression="^(AES_CM_128_HMAC_SHA1_32|AES_CM_128_HMAC_SHA1_80)$"> <action application="sched_hangup" data="+10 NORMAL_CLEARING"/>
<action application="answer" data=""/>
<action application="set" data="cdr_delete=1"/>
<action application="execute_extension" data="is_secure XML ${context}"/>
<action application="playback" data="${hold_music}"/>
<action application="set" data="lcr_user_second_rate=0"/>
<action application="set" data="lcr_user_rate=0"/>
<anti-action application="sched_hangup" data="+10 NORMAL_CLEARING"/>
<anti-action application="set" data="zrtp_secure_media=true"/>
<anti-action application="answer" data=""/>
<anti-action application="playback" data="silence_stream://2000"/>
<anti-action application="execute_extension" data="is_zrtp_secure XML ${context}"/>
<anti-action application="set" data="lcr_user_second_rate=0"/>
<anti-action application="set" data="lcr_user_rate=0"/>
<anti-action application="set" data="cdr_delete=1"/>
<anti-action application="playback" data="${hold_music}"/>
</condition>
</extension>

Pay attention to the new variables.

Making the Leg-B Trick Optional

So, at this point, the 10 seconds workaround will apply to all the calls. If you are a small company or your PBX is just for personal use, you probably would be fine at this point. But, if you have more customers you may want to save money when the call length is longer than six seconds, there is no need to pay extra to the carriers.

The tricky thing here is that you must detect the call length when the leg-a has hung up. The first idea that came to my mind was using the billsec variable; naive of me, the billsec variable is not available at that point.

My next idea was using the uuid_setvar FreeSWITCH API call; I was able to capture the epoch when the call was answered with a line like this: export nolocal:api_on_answer=uuid_setvar ${uuid} a_epoch ${strepoch()}. The issue here was to capture the epoch when leg-a hangs up. My first attempt was using the api_hangup_hook pointing to an LUA script. I was wrong. Although I was able to capture the epoch, I had to make that new variable value available in the leg b or to be able to set the exec_after_bridge_* variables in leg b (which is still alive). Since api_hangup_hook could only give me a read-only session copy or leg a, and as far as I was digging I couldn't figure out the UUID of leg b, this approach was useless.

Finally, I found a solution!, by adding an export nolocal:execute_on_answer=setvar.lua action, the script would be executed inside the leg b workspace. I wouldn't need to know the current leg b UUID and by marking the answering time, I was able to later do some maths on leg b transfer to play the music or just hang up. The LUA script was as simple as: session:execute("set", "a_epoch=${strepoch()}");

The only missing part was adding more complexity to the *long dialplan to do the logic. It was something like this:

<extension name="long" continue="false" uuid="916473cf-68e3-43d2-afeb-63e5fa86653d">
<condition field="destination_number" expression="^\*long$">
<action application="set" data="b_lenght=${expr(${strepoch()} - ${a_epoch})}" inline="true"/>
<action application="set" data="cdr_delete=1"/>
</condition>
<condition field="destination_number" expression="^\*long$"/>
<condition field="${expr(above(${b_lenght},6)}" expression="0">
<action application="sched_hangup" data="+10 NORMAL_CLEARING"/>
</condition>
<condition field="destination_number" expression="^\*long$"/>
<condition field="${sip_has_crypto}" expression="^(AES_CM_128_HMAC_SHA1_32|AES_CM_128_HMAC_SHA1_80)$">
<action application="answer" data=""/>
<action application="set" data="cdr_delete=1"/>
<action application="execute_extension" data="is_secure XML ${context}"/>
<action application="playback" data="${hold_music}"/>
<action application="set" data="lcr_user_second_rate=0"/>
<action application="set" data="lcr_user_rate=0"/>
<anti-action application="set" data="zrtp_secure_media=true"/>
<anti-action application="answer" data=""/>
<anti-action application="playback" data="silence_stream://2000"/>
<anti-action application="execute_extension" data="is_zrtp_secure XML ${context}"/>
<anti-action application="set" data="lcr_user_second_rate=0"/>
<anti-action application="set" data="lcr_user_rate=0"/>
<anti-action application="set" data="cdr_delete=1"/>
<anti-action application="playback" data="${hold_music}"/>
</condition>
</extension>
 
<extension name="lcr.1d10" continue="false" uuid="f6b2ee23-a656-4c13-9765-4502946d3d5c">
<condition field="destination_number" expression="^\+?1?(\d{10})$">
<action application="set" data="sip_h_X-accountcode=${accountcode}"/>
<action application="set" data="call_direction=outbound"/>
<action application="set" data="hangup_after_bridge=true"/>
<action application="set" data="effective_caller_id_name=${outbound_caller_id_name}"/>
<action application="set" data="effective_caller_id_number=${outbound_caller_id_number}" inline="true"/>
<action application="set" data="inherit_codec=true"/>
<action application="set" data="ignore_display_updates=true"/>
<action application="set" data="callee_id_number=$1"/>
<action application="set" data="session_in_hangup_hook=true"/>
<action application="set" data="continue_on_fail=true"/>
<action application="export" data="nolocal:exec_after_bridge_app=transfer"/>
<action application="export" data="nolocal:exec_after_bridge_arg=*long"/>
<action application="export" data="nolocal:execute_on_answer=lua setvar.lua"/>
<action application="bridge" data="lcr/default/1$1"/>
</condition>
</extension>

That's it. The next time you call and hang up right away, the call will be scheduled to hang up in 10 seconds. Long enough to avoid the fee.

Good luck!

blog comments powered by Disqus

About

Read about IT, Migration, Business, Money, Marketing and other subjects.

Some subjects: FusionPBX, FreeSWITCH, Linux, Security, Canada, Cryptocurrency, Trading.