Outbound Calling: Rate Limit or Queue (Not a Call Queue)

I would like to rate-limit outbound calling, keeping the call processing limited to twenty (20) calls per second. If more than twenty (20) calls are attempted, only twenty (20) are processed, the remaining calls are queued and the next twenty (20) are placed a second later. FIFO

I have a service provider that will shut down the SIP trunk if it receives more than twenty (20) outbound calls in one (1) second. This is a toll-fraud prevention mechanism, the service provider will not change it.

The trunk supports over a hundred channels, so call-limit is not an acceptable solution. I would like the calls to be placed, waiting a second or two (2) is acceptable.

I can control this using a predictive dialer, but I do not have control over regular users. So I am looking for a way to rate-limit at the trunk itself.

Does anyone have an idea how to accomplish this?

I considered an AGI, somehow queuing each request, putting the request into a FIFO queue, than releasing 20 per second back to the dailplan, where the outbound dial command is next in the context. Easier said, than done!

Any help or suggestions would be appreciated.

I guess you could use group counts with the second number being the resource name.

Have you considered that you will also rate limit toll fraudsters, so that the service provider’s protection system will not trigger and you will bear the cost of an extended attack.

1 Like

@david551

At first, I wasn’t sure what you’re suggesting. But found a solution after giving it a little thought.

By setting the GROUP_COUNT() resource as ${EPOCH}, I can count the calls placed in the current second.

From there, it’s a simple regular expression to determine if the number of calls exceeds the target calling rate.

Sending calls that exceed the target to another context to wait for a fraction of a second, before trying again.
Otherwise, the call continues and will Dial() the providers trunk.

[globals]
calls_per_sec=20

[OUTBOUND]
exten => _X.,1,NoOp(Rate Limited Calling)
 same => n,Set(GROUP()=${EPOCH})
 same => n,GotoIf($[${GROUP_COUNT(${EPOCH})}>${calls_per_sec}]?DELAY,${EXTEN},1)
 same => n,Dial(SIP/provider/${EXTEN})
 
[DELAY]
exten => _X.,1,NoOp(Half Second Delay)
 same => n,Wait(0.5)
 same => n,Goto(OUTBOUND,${EXTEN},1)

I understand the risks of bypassing the providers system, have my own toll-fraud protections in place too.

Thank you!

I don’t know how memory is managed for group count, but, be on the safe side, I would use a modulo reduction of epoch, to avoid any risk of bleeding memory.

@david551

I actually started with the STRFTIME() function. Since ${STRFTIME(${EPOCH},,%H%M%S)}) uses ${EPOCH} and I was only interested in the seconds, ${EPOCH} seemed like the best approach.

I added the following line after the Dial() application. To ensure GROUP_COUNT() does not decrease during the current second.

 same => n,WaitUntil($[${EPOCH}+1])

${EPOCH} works natively with WaitUntil().

But I will keep and eye on the memory.

Thanks!