關於這個功能,esl-client 上給出的源碼示例極具誤導性,根本跑不起來,見: https://github.com/esl-client/esl-client/blob/master/src/test/java/OutboundTest.java
正確姿勢:必須在事件訂閱的回調里,才能拿到用戶按鍵值
示例代碼:
package org.freeswitch.esl.client; import org.freeswitch.esl.client.dptools.Execute; import org.freeswitch.esl.client.dptools.ExecuteException; import org.freeswitch.esl.client.internal.Context; import org.freeswitch.esl.client.outbound.IClientHandler; import org.freeswitch.esl.client.outbound.IClientHandlerFactory; import org.freeswitch.esl.client.outbound.SocketClient; import org.freeswitch.esl.client.transport.event.EslEvent; import org.freeswitch.esl.client.transport.message.EslHeaders.Name; import org.freeswitch.esl.client.transport.message.EslMessage; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.net.InetSocketAddress; import java.util.regex.Pattern; import static com.google.common.base.Throwables.throwIfUnchecked; public class OutboundDTMFTest { private static Logger logger = LoggerFactory.getLogger(OutboundDTMFTest.class); private static String sb = "/usr/local/freeswitch/sounds/en/us/callie/ivr/8000/"; String prompt = sb + "ivr-please_enter_extension_followed_by_pound.wav"; String failed = sb + "ivr-that_was_an_invalid_entry.wav"; public static void main(String[] args) { new OutboundDTMFTest(); } public OutboundDTMFTest() { try { //outbound test final SocketClient outboundServer = new SocketClient( new InetSocketAddress("localhost", 8086), new OutboundHandlerFactory()); outboundServer.startAsync(); } catch (Throwable t) { throwIfUnchecked(t); } } public class OutboundHandlerFactory implements IClientHandlerFactory { @Override public IClientHandler createClientHandler() { //just for sample , recommend use singleton pattern, to avoid new too many instance return new OutboundHandler(); } } public class OutboundHandler implements IClientHandler { StringBuffer buffer = new StringBuffer(10); String pattern1 = "^\\d+"; String pattern2 = "^\\d+#$"; @Override public void onConnect(Context context, EslEvent eslEvent) { try { Execute exe = new Execute(context, ""); //訂閱DTMF事件 EslMessage eslMessage = context.sendCommand("event plain DTMF"); if (eslMessage.getHeaderValue(Name.REPLY_TEXT).startsWith("+OK")) { logger.info("subscribe event success!"); } exe.answer(); int timeOutSeconds = 30; //放音采集 exe.playAndGetDigits(1, 1, 10, timeOutSeconds * 1000, "#", prompt, failed, pattern1, timeOutSeconds * 1000); //等待用戶輸入按鍵 long start = System.currentTimeMillis(); while (true) { if (System.currentTimeMillis() - start > timeOutSeconds * 1000) { break; } if (buffer.length() > 0 && Pattern.matches(pattern2, buffer.toString())) { break; } Thread.sleep(50); } System.out.println("you pressed:" + buffer.toString()); } catch (ExecuteException | InterruptedException e) { logger.error("Could not prompt for digits", e); } finally { context.closeChannel(); } } @Override public void onEslEvent(Context ctx, EslEvent event) { // System.out.println(event.getEventName()); if (event.getEventName().equalsIgnoreCase("DTMF")) { String key = event.getEventHeaders().get("DTMF-Digit"); if ("#".equalsIgnoreCase(key)) { //檢查是否輸入正確(如果錯誤,請將之前輸入的清空掉) if (!Pattern.matches(pattern1, buffer.toString())) { buffer.setLength(0); return; } } buffer.append(key); } } } }
解釋一下:
1. 首先要訂閱DTMF事件,只有在事件回調里,才能拿到用戶按鍵信息
2. playAndGetDigits 在outbound async full異步模式下,這個方法的返回值,其實沒啥用,永遠都是__undef__,所以要在后面循環檢測結果,還要考慮用戶一直不按鍵的情況,要有超時保底
3. 事件回調onEslEvent與用戶進線onConnect是在2個不同的方法中,但是都是在同一個線程里的,所以為方便起見,用了一個線程安全的StringBuffer用來保存按鍵信息
4. 事件回調中,要考慮用戶按錯鍵的情況,比如提示用戶按數字鍵,然后用戶輸入了字母或星號之類的,遇到這種要把之前的輸入結果清掉。