From 5af63742655f9e4b9e37bb427f86ed1c68153761 Mon Sep 17 00:00:00 2001 From: Richard Alam <ritzalam@gmail.com> Date: Wed, 5 Feb 2020 14:58:47 -0800 Subject: [PATCH] Track call session state Currently, we user DTMF to inform the client when the call session is in echo test and when entering the voice conference. Unfortunately, sometimes when FS sends the DTMF, FS crashes. Monitor the progress of the call session using ESL events and propagate to the client. The client would be informed of these call states: CALL_STARTED, IN_ECHO_TEST, IN_CONFERENCE, CALL_ENDED. --- .../core/apps/voice/VoiceApp2x.scala | 8 + .../voice/VoiceConfCallStateEvtMsgHdlr.scala | 42 ++++ .../senders/ReceivedJsonMsgHandlerActor.scala | 2 + .../core/running/MeetingActor.scala | 1 + .../bigbluebutton/core2/AnalyticsActor.scala | 2 + .../FreeswitchConferenceEventListener.java | 13 ++ .../voice/IVoiceConferenceService.java | 10 + .../events/FreeswitchStatusReplyEvent.java | 11 + .../voice/events/VoiceCallStateEvent.java | 30 +++ .../voice/freeswitch/ConnectionManager.java | 17 +- .../voice/freeswitch/ESLEventListener.java | 209 +++++++++++++++++- .../actions/CheckFreeswitchStatusCommand.java | 6 +- .../freeswitch/VoiceConferenceService.scala | 37 ++++ .../common2/msgs/VoiceConfMsgs.scala | 38 +++- .../freeswitch/esl/client/inbound/Client.java | 5 +- 15 files changed, 416 insertions(+), 15 deletions(-) create mode 100755 akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/voice/VoiceConfCallStateEvtMsgHdlr.scala create mode 100755 akka-bbb-fsesl/src/main/java/org/bigbluebutton/freeswitch/voice/events/FreeswitchStatusReplyEvent.java create mode 100755 akka-bbb-fsesl/src/main/java/org/bigbluebutton/freeswitch/voice/events/VoiceCallStateEvent.java diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/voice/VoiceApp2x.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/voice/VoiceApp2x.scala index 571c4db2bd..c5f0662066 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/voice/VoiceApp2x.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/voice/VoiceApp2x.scala @@ -3,6 +3,13 @@ package org.bigbluebutton.core.apps.voice import org.bigbluebutton.core.running.MeetingActor import org.bigbluebutton.core2.message.handlers.RecordingStartedVoiceConfEvtMsgHdlr +object VoiceCallState { + val NOT_IN_CALL = "NOT_IN_CALL" + val IN_ECHO_TEST = "IN_ECHO_TEST" + val IN_CONFERENCE = "IN_CONFERENCE" + val CALL_STARTED = "CALL_STARTED" + val CALL_ENDED = "CALL_ENDED" +} trait VoiceApp2x extends UserJoinedVoiceConfEvtMsgHdlr with UserJoinedVoiceConfMessageHdlr with UserLeftVoiceConfEvtMsgHdlr @@ -11,6 +18,7 @@ trait VoiceApp2x extends UserJoinedVoiceConfEvtMsgHdlr with RecordingStartedVoiceConfEvtMsgHdlr with VoiceConfRunningEvtMsgHdlr with SyncGetVoiceUsersMsgHdlr + with VoiceConfCallStateEvtMsgHdlr with UserStatusVoiceConfEvtMsgHdlr { this: MeetingActor => diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/voice/VoiceConfCallStateEvtMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/voice/VoiceConfCallStateEvtMsgHdlr.scala new file mode 100755 index 0000000000..b82ddc6468 --- /dev/null +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/voice/VoiceConfCallStateEvtMsgHdlr.scala @@ -0,0 +1,42 @@ +package org.bigbluebutton.core.apps.voice + +import org.bigbluebutton.common2.msgs.{ BbbClientMsgHeader, BbbCommonEnvCoreMsg, BbbCoreEnvelope, MessageTypes, Routing, VoiceCallStateEvtMsg, VoiceCallStateEvtMsgBody, VoiceConfCallStateEvtMsg } +import org.bigbluebutton.core.models.{ VoiceUserState, VoiceUsers } +import org.bigbluebutton.core.running.{ LiveMeeting, MeetingActor, OutMsgRouter } + +trait VoiceConfCallStateEvtMsgHdlr { + this: MeetingActor => + + val liveMeeting: LiveMeeting + val outGW: OutMsgRouter + + def handleVoiceConfCallStateEvtMsg(msg: VoiceConfCallStateEvtMsg): Unit = { + val routing = Routing.addMsgToClientRouting( + MessageTypes.BROADCAST_TO_MEETING, + liveMeeting.props.meetingProp.intId, + msg.body.voiceConf + ) + val envelope = BbbCoreEnvelope( + VoiceCallStateEvtMsg.NAME, + routing + ) + val header = BbbClientMsgHeader( + VoiceCallStateEvtMsg.NAME, + liveMeeting.props.meetingProp.intId, + msg.body.voiceConf + ) + + val body = VoiceCallStateEvtMsgBody( + meetingId = liveMeeting.props.meetingProp.intId, + voiceConf = msg.body.voiceConf, + clientSession = msg.body.callSession, + userId = msg.body.userId, + callerName = msg.body.callerName, + callState = msg.body.callState + ) + + val event = VoiceCallStateEvtMsg(header, body) + val msgEvent = BbbCommonEnvCoreMsg(envelope, event) + outGW.send(msgEvent) + } +} diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/pubsub/senders/ReceivedJsonMsgHandlerActor.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/pubsub/senders/ReceivedJsonMsgHandlerActor.scala index 7dd24a7fa9..2db88ad68a 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/pubsub/senders/ReceivedJsonMsgHandlerActor.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/pubsub/senders/ReceivedJsonMsgHandlerActor.scala @@ -152,6 +152,8 @@ class ReceivedJsonMsgHandlerActor( routeVoiceMsg[CheckRunningAndRecordingVoiceConfEvtMsg](envelope, jsonNode) case UserStatusVoiceConfEvtMsg.NAME => routeVoiceMsg[UserStatusVoiceConfEvtMsg](envelope, jsonNode) + case VoiceConfCallStateEvtMsg.NAME => + routeVoiceMsg[VoiceConfCallStateEvtMsg](envelope, jsonNode) // Breakout rooms case BreakoutRoomsListMsg.NAME => diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingActor.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingActor.scala index b6e32aab55..5ea402cdde 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingActor.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/running/MeetingActor.scala @@ -384,6 +384,7 @@ class MeetingActor( state = updateInactivityTracker(state) updateVoiceUserLastActivity(m.body.voiceUserId) handleUserTalkingInVoiceConfEvtMsg(m) + case m: VoiceConfCallStateEvtMsg => handleVoiceConfCallStateEvtMsg(m) case m: RecordingStartedVoiceConfEvtMsg => handleRecordingStartedVoiceConfEvtMsg(m) case m: MuteUserCmdMsg => diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/AnalyticsActor.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/AnalyticsActor.scala index c7b4b09486..f65e7bf727 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/AnalyticsActor.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/AnalyticsActor.scala @@ -93,6 +93,8 @@ class AnalyticsActor extends Actor with ActorLogging { // Voice case m: UserMutedVoiceEvtMsg => logMessage(msg) + case m: VoiceConfCallStateEvtMsg => logMessage(msg) + case m: VoiceCallStateEvtMsg => logMessage(msg) // Breakout case m: BreakoutRoomEndedEvtMsg => logMessage(msg) diff --git a/akka-bbb-fsesl/src/main/java/org/bigbluebutton/freeswitch/voice/FreeswitchConferenceEventListener.java b/akka-bbb-fsesl/src/main/java/org/bigbluebutton/freeswitch/voice/FreeswitchConferenceEventListener.java index 0d165a9a5c..48012683bf 100755 --- a/akka-bbb-fsesl/src/main/java/org/bigbluebutton/freeswitch/voice/FreeswitchConferenceEventListener.java +++ b/akka-bbb-fsesl/src/main/java/org/bigbluebutton/freeswitch/voice/FreeswitchConferenceEventListener.java @@ -100,6 +100,19 @@ public class FreeswitchConferenceEventListener implements ConferenceEventListene } else if (event instanceof VoiceUsersStatusEvent) { VoiceUsersStatusEvent evt = (VoiceUsersStatusEvent) event; vcs.voiceUsersStatus(evt.getRoom(), evt.confMembers, evt.confRecordings); + } else if (event instanceof VoiceCallStateEvent) { + VoiceCallStateEvent evt = (VoiceCallStateEvent) event; + vcs.voiceCallStateEvent(evt.getRoom(), + evt.callSession, + evt.clientSession, + evt.userId, + evt.callerName, + evt.callState, + evt.origCallerIdName, + evt.origCalledDest); + } else if (event instanceof FreeswitchStatusReplyEvent) { + FreeswitchStatusReplyEvent evt = (FreeswitchStatusReplyEvent) event; + vcs.freeswitchStatusReplyEvent(evt.jsonResponse); } } diff --git a/akka-bbb-fsesl/src/main/java/org/bigbluebutton/freeswitch/voice/IVoiceConferenceService.java b/akka-bbb-fsesl/src/main/java/org/bigbluebutton/freeswitch/voice/IVoiceConferenceService.java index e916924939..cd42bfbaa1 100755 --- a/akka-bbb-fsesl/src/main/java/org/bigbluebutton/freeswitch/voice/IVoiceConferenceService.java +++ b/akka-bbb-fsesl/src/main/java/org/bigbluebutton/freeswitch/voice/IVoiceConferenceService.java @@ -65,4 +65,14 @@ public interface IVoiceConferenceService { Boolean isRecording, java.util.List<ConfRecording> confRecording); + void voiceCallStateEvent(String conf, + String callSession, + String clientSession, + String userId, + String callerName, + String callState, + String origCallerIdName, + String origCalledDest); + + void freeswitchStatusReplyEvent(String json); } diff --git a/akka-bbb-fsesl/src/main/java/org/bigbluebutton/freeswitch/voice/events/FreeswitchStatusReplyEvent.java b/akka-bbb-fsesl/src/main/java/org/bigbluebutton/freeswitch/voice/events/FreeswitchStatusReplyEvent.java new file mode 100755 index 0000000000..d8ae7f3541 --- /dev/null +++ b/akka-bbb-fsesl/src/main/java/org/bigbluebutton/freeswitch/voice/events/FreeswitchStatusReplyEvent.java @@ -0,0 +1,11 @@ +package org.bigbluebutton.freeswitch.voice.events; + +public class FreeswitchStatusReplyEvent extends VoiceConferenceEvent { + + public final String jsonResponse; + + public FreeswitchStatusReplyEvent(String json) { + super("unused"); + this.jsonResponse = json; + } +} diff --git a/akka-bbb-fsesl/src/main/java/org/bigbluebutton/freeswitch/voice/events/VoiceCallStateEvent.java b/akka-bbb-fsesl/src/main/java/org/bigbluebutton/freeswitch/voice/events/VoiceCallStateEvent.java new file mode 100755 index 0000000000..9d0a9277e6 --- /dev/null +++ b/akka-bbb-fsesl/src/main/java/org/bigbluebutton/freeswitch/voice/events/VoiceCallStateEvent.java @@ -0,0 +1,30 @@ +package org.bigbluebutton.freeswitch.voice.events; + +public class VoiceCallStateEvent extends VoiceConferenceEvent { + public final String callSession; + public final String clientSession; + public final String userId; + public final String callerName; + public final String callState; + public final String origCallerIdName; + public final String origCalledDest; + + public VoiceCallStateEvent( + String conf, + String callSession, + String clientSession, + String userId, + String callerName, + String callState, + String origCallerIdName, + String origCalledDest) { + super(conf); + this.callSession = callSession; + this.clientSession = clientSession; + this.userId = userId; + this.callerName = callerName; + this.callState = callState; + this.origCallerIdName = origCallerIdName; + this.origCalledDest = origCalledDest; + } +} diff --git a/akka-bbb-fsesl/src/main/java/org/bigbluebutton/freeswitch/voice/freeswitch/ConnectionManager.java b/akka-bbb-fsesl/src/main/java/org/bigbluebutton/freeswitch/voice/freeswitch/ConnectionManager.java index 47a6b8e32e..a194ffa16c 100755 --- a/akka-bbb-fsesl/src/main/java/org/bigbluebutton/freeswitch/voice/freeswitch/ConnectionManager.java +++ b/akka-bbb-fsesl/src/main/java/org/bigbluebutton/freeswitch/voice/freeswitch/ConnectionManager.java @@ -34,6 +34,7 @@ import org.bigbluebutton.freeswitch.voice.freeswitch.actions.*; import org.freeswitch.esl.client.inbound.Client; import org.freeswitch.esl.client.inbound.InboundConnectionFailure; import org.freeswitch.esl.client.manager.ManagerConnection; +import org.freeswitch.esl.client.transport.CommandResponse; import org.freeswitch.esl.client.transport.message.EslMessage; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -81,10 +82,18 @@ public class ConnectionManager { if (!subscribed) { log.info("Subscribing for ESL events."); c.cancelEventSubscriptions(); - c.setEventSubscriptions("plain", "all"); - //c.addEventFilter(EVENT_NAME, "heartbeat"); - c.addEventFilter(EVENT_NAME, "custom"); - c.addEventFilter(EVENT_NAME, "background_job"); + CommandResponse response = c.setEventSubscriptions("plain", "all"); + if (response.isOk()) { + log.info("Subscribed to ESL events." + + " Command: [" + response.getCommand() + "] " + + " Response: [" + response.getReplyText() + "]"); + } + + //c.addEventFilter(EVENT_NAME, "HEARTBEAT"); + //c.addEventFilter(EVENT_NAME, "custom"); + //c.addEventFilter(EVENT_NAME, "background_job"); + c.addEventFilter(EVENT_NAME, "CHANNEL_EXECUTE"); + c.addEventFilter(EVENT_NAME, "CHANNEL_STATE"); subscribed = true; } else { // Let's check for status every minute. diff --git a/akka-bbb-fsesl/src/main/java/org/bigbluebutton/freeswitch/voice/freeswitch/ESLEventListener.java b/akka-bbb-fsesl/src/main/java/org/bigbluebutton/freeswitch/voice/freeswitch/ESLEventListener.java index d18b994b21..fb204b984d 100755 --- a/akka-bbb-fsesl/src/main/java/org/bigbluebutton/freeswitch/voice/freeswitch/ESLEventListener.java +++ b/akka-bbb-fsesl/src/main/java/org/bigbluebutton/freeswitch/voice/freeswitch/ESLEventListener.java @@ -1,6 +1,7 @@ package org.bigbluebutton.freeswitch.voice.freeswitch; +import java.util.Iterator; import java.util.Map; import java.util.concurrent.TimeUnit; import java.util.regex.Matcher; @@ -52,6 +53,8 @@ public class ESLEventListener implements IEslEventListener { private static final Pattern GLOBAL_AUDION_PATTERN = Pattern.compile("(GLOBAL_AUDIO)_(.*)$"); private static final Pattern CALLERNAME_PATTERN = Pattern.compile("(.*)-bbbID-(.*)$"); private static final Pattern CALLERNAME_WITH_SESS_INFO_PATTERN = Pattern.compile("^(.*)_(\\d+)-bbbID-(.*)$"); + private static final Pattern CALLERNAME_LISTENONLY_PATTERN = Pattern.compile("^(.*)_(\\d+)-bbbID-LISTENONLY-(.*)$"); + private static final Pattern ECHO_TEST_DEST_PATTERN = Pattern.compile("^9196(\\d+)$"); @Override public void conferenceEventJoin(String uniqueId, String confName, int confSize, EslEvent event) { @@ -92,6 +95,22 @@ public class ESLEventListener implements IEslEventListener { voiceUserId = "v_" + memberId.toString(); } + String coreuuid = headers.get("Core-UUID"); + String clientSession = "0"; + String callState = "IN_CONFERENCE"; + String origCallerIdName = headers.get("Caller-Caller-ID-Name"); + String origCallerDestNumber = headers.get("Caller-Destination-Number"); + VoiceCallStateEvent csEvent = new VoiceCallStateEvent( + confName, + coreuuid, + clientSession, + voiceUserId, + callerIdName, + callState, + origCallerIdName, + origCallerDestNumber); + conferenceEventListener.handleConferenceEvent(csEvent); + String callerUUID = this.getMemberUUIDFromEvent(event); log.info("Caller joined: conf=" + confName + ",uuid=" + callerUUID + @@ -111,6 +130,8 @@ public class ESLEventListener implements IEslEventListener { speaking, "none"); conferenceEventListener.handleConferenceEvent(pj); + + } } @@ -243,13 +264,187 @@ public class ESLEventListener implements IEslEventListener { @Override public void eventReceived(EslEvent event) { - //System.out.println("ESL Event Listener received event=[" + event.getEventName() + "]" + - // event.getEventHeaders().toString()); - if (event.getEventName().equals("heartbeat")) { - log.info("Received heartbeat from FreeSWITCH"); -//// setChanged(); -// notifyObservers(event); -// return; +// System.out.println("*********** ESL Event Listener received event=[" + event.getEventName() + "]" + +// event.getEventHeaders().toString()); + + /** + Map<String, String> eventHeaders1 = event.getEventHeaders(); + StringBuilder sb = new StringBuilder(""); + sb.append("\n"); + for (Iterator it = eventHeaders1.entrySet().iterator(); it.hasNext(); ) { + Map.Entry entry = (Map.Entry)it.next(); + sb.append(entry.getKey()); + sb.append(" => '"); + sb.append(entry.getValue()); + sb.append("'\n"); + } + + System.out.println("##### ===>>> " + sb.toString()); + System.out.println("<<<=== #####"); + **/ + + if (event.getEventName().equals("HEARTBEAT")) { + //log.info("Received heartbeat from FreeSWITCH"); + } else if (event.getEventName().equals( "CHANNEL_EXECUTE" )) { + Map<String, String> eventHeaders = event.getEventHeaders(); + + String application = (eventHeaders.get("Application") == null) ? "" : eventHeaders.get("Application"); + String channelCallState = (eventHeaders.get("Channel-Call-State") == null) ? "" : eventHeaders.get("Channel-Call-State"); + String varvBridge = (eventHeaders.get("variable_vbridge") == null) ? "" : eventHeaders.get("variable_vbridge"); + + if ("echo".equalsIgnoreCase(application) && !varvBridge.isEmpty()) { + String origCallerIdName = eventHeaders.get("Caller-Caller-ID-Name"); + String origCallerDestNumber = eventHeaders.get("Caller-Destination-Number"); + String coreuuid = eventHeaders.get("Core-UUID"); + + //System.out.println("******** uuid=" + coreuuid + " " + origCallerIdName + " is in echo test " + origCallerDestNumber + " vbridge=" + varvBridge); + + String voiceUserId = ""; + String callerName = origCallerIdName; + String clientSession = "0"; + String callState = "IN_ECHO_TEST"; + + Matcher callerListenOnly = CALLERNAME_LISTENONLY_PATTERN.matcher(origCallerIdName); + Matcher callWithSess = CALLERNAME_WITH_SESS_INFO_PATTERN.matcher(origCallerIdName); + if (callWithSess.matches()) { + voiceUserId = callWithSess.group(1).trim(); + clientSession = callWithSess.group(2).trim(); + callerName = callWithSess.group(3).trim(); + } else if (callerListenOnly.matches()) { + voiceUserId = callerListenOnly.group(1).trim(); + clientSession = callWithSess.group(2).trim(); + callerName = callerListenOnly.group(3).trim(); + } + + VoiceCallStateEvent csEvent = new VoiceCallStateEvent(varvBridge, + coreuuid, + clientSession, + voiceUserId, + callerName, + callState, + origCallerIdName, + origCallerDestNumber); + conferenceEventListener.handleConferenceEvent(csEvent); + + } else if ("RINGING".equalsIgnoreCase(channelCallState) && !varvBridge.isEmpty()) { + String origCallerIdName = eventHeaders.get("Caller-Caller-ID-Name"); + String origCallerDestNumber = eventHeaders.get("Caller-Destination-Number"); + String coreuuid = eventHeaders.get("Core-UUID"); + //System.out.println("******** uuid=" + coreuuid + " " + origCallerIdName + " is in ringing " + origCallerDestNumber + " vbridge=" + varvBridge); + + String voiceUserId = ""; + String callerName = origCallerIdName; + String clientSession = "0"; + String callState = "CALL_STARTED"; + + Matcher callerListenOnly = CALLERNAME_LISTENONLY_PATTERN.matcher(origCallerIdName); + Matcher callWithSess = CALLERNAME_WITH_SESS_INFO_PATTERN.matcher(origCallerIdName); + if (callWithSess.matches()) { + voiceUserId = callWithSess.group(1).trim(); + clientSession = callWithSess.group(2).trim(); + callerName = callWithSess.group(3).trim(); + } else if (callerListenOnly.matches()) { + voiceUserId = callerListenOnly.group(1).trim(); + clientSession = callWithSess.group(2).trim(); + callerName = callerListenOnly.group(3).trim(); + } + + VoiceCallStateEvent csEvent = new VoiceCallStateEvent(varvBridge, + coreuuid, + clientSession, + voiceUserId, + callerName, + callState, + origCallerIdName, + origCallerDestNumber); + conferenceEventListener.handleConferenceEvent(csEvent); + } + } else if (event.getEventName().equalsIgnoreCase("CHANNEL_STATE")) { + Map<String, String> eventHeaders = event.getEventHeaders(); + String channelCallState = (eventHeaders.get("Channel-Call-State") == null) ? "" : eventHeaders.get("Channel-Call-State"); + String channelState = (eventHeaders.get("Channel-State") == null) ? "" : eventHeaders.get("Channel-State"); + + if ("HANGUP".equalsIgnoreCase(channelCallState) && "CS_DESTROY".equalsIgnoreCase(channelState)) { + String origCallerIdName = eventHeaders.get("Caller-Caller-ID-Name"); + String origCallerDestNumber = eventHeaders.get("Caller-Destination-Number"); + String coreuuid = eventHeaders.get("Core-UUID"); + //System.out.println("******** uuid=" + coreuuid + " " + origCallerIdName + " is hanging up " + origCallerDestNumber); + + String voiceUserId = ""; + String callerName = origCallerIdName; + String clientSession = "0"; + String callState = "CALL_ENDED"; + + Matcher callerListenOnly = CALLERNAME_LISTENONLY_PATTERN.matcher(origCallerIdName); + Matcher callWithSess = CALLERNAME_WITH_SESS_INFO_PATTERN.matcher(origCallerIdName); + if (callWithSess.matches()) { + voiceUserId = callWithSess.group(1).trim(); + clientSession = callWithSess.group(2).trim(); + callerName = callWithSess.group(3).trim(); + } else if (callerListenOnly.matches()) { + voiceUserId = callerListenOnly.group(1).trim(); + clientSession = callWithSess.group(2).trim(); + callerName = callerListenOnly.group(3).trim(); + } + + String conf = origCallerDestNumber; + Matcher callerDestNumberMatcher = ECHO_TEST_DEST_PATTERN.matcher(origCallerDestNumber); + if (callerDestNumberMatcher.matches()) { + conf = callerDestNumberMatcher.group(1).trim(); + } + + VoiceCallStateEvent csEvent = new VoiceCallStateEvent(conf, + coreuuid, + clientSession, + voiceUserId, + callerName, + callState, + origCallerIdName, + origCallerDestNumber + ); + conferenceEventListener.handleConferenceEvent(csEvent); + + } else if ("RINGING".equalsIgnoreCase(channelCallState) && "CS_EXECUTE".equalsIgnoreCase(channelState)) { + String origCallerIdName = eventHeaders.get("Caller-Caller-ID-Name"); + String origCallerDestNumber = eventHeaders.get("Caller-Destination-Number"); + String coreuuid = eventHeaders.get("Core-UUID"); + //System.out.println("******** uuid=" + coreuuid + " " + origCallerIdName + " is ringing " + origCallerDestNumber); + + String voiceUserId = ""; + String callerName = origCallerIdName; + String clientSession = "0"; + String callState = "CALL_STARTED"; + + Matcher callerListenOnly = CALLERNAME_LISTENONLY_PATTERN.matcher(origCallerIdName); + Matcher callWithSess = CALLERNAME_WITH_SESS_INFO_PATTERN.matcher(origCallerIdName); + if (callWithSess.matches()) { + voiceUserId = callWithSess.group(1).trim(); + clientSession = callWithSess.group(2).trim(); + callerName = callWithSess.group(3).trim(); + } else if (callerListenOnly.matches()) { + voiceUserId = callerListenOnly.group(1).trim(); + clientSession = callWithSess.group(2).trim(); + callerName = callerListenOnly.group(3).trim(); + } + + String conf = origCallerDestNumber; + Matcher callerDestNumberMatcher = ECHO_TEST_DEST_PATTERN.matcher(origCallerDestNumber); + if (callerDestNumberMatcher.matches()) { + conf = callerDestNumberMatcher.group(1).trim(); + } + + VoiceCallStateEvent csEvent = new VoiceCallStateEvent(conf, + coreuuid, + clientSession, + voiceUserId, + callerName, + callState, + origCallerIdName, + origCallerDestNumber + ); + conferenceEventListener.handleConferenceEvent(csEvent); + } + } } diff --git a/akka-bbb-fsesl/src/main/java/org/bigbluebutton/freeswitch/voice/freeswitch/actions/CheckFreeswitchStatusCommand.java b/akka-bbb-fsesl/src/main/java/org/bigbluebutton/freeswitch/voice/freeswitch/actions/CheckFreeswitchStatusCommand.java index d0d2aa67f0..2e8e4f912d 100755 --- a/akka-bbb-fsesl/src/main/java/org/bigbluebutton/freeswitch/voice/freeswitch/actions/CheckFreeswitchStatusCommand.java +++ b/akka-bbb-fsesl/src/main/java/org/bigbluebutton/freeswitch/voice/freeswitch/actions/CheckFreeswitchStatusCommand.java @@ -3,6 +3,7 @@ package org.bigbluebutton.freeswitch.voice.freeswitch.actions; import com.google.gson.Gson; import org.apache.commons.lang3.StringUtils; import org.bigbluebutton.freeswitch.voice.events.ConferenceEventListener; +import org.bigbluebutton.freeswitch.voice.events.FreeswitchStatusReplyEvent; import org.freeswitch.esl.client.transport.message.EslMessage; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -26,10 +27,11 @@ public class CheckFreeswitchStatusCommand extends FreeswitchCommand { } public void handleResponse(EslMessage response, ConferenceEventListener eventListener) { - Gson gson = new Gson(); log.info(gson.toJson(response.getBodyLines())); - + FreeswitchStatusReplyEvent statusEvent = new FreeswitchStatusReplyEvent( + gson.toJson(response.getBodyLines())); + eventListener.handleConferenceEvent(statusEvent); } } diff --git a/akka-bbb-fsesl/src/main/scala/org/bigbluebutton/freeswitch/VoiceConferenceService.scala b/akka-bbb-fsesl/src/main/scala/org/bigbluebutton/freeswitch/VoiceConferenceService.scala index 2e186f1990..07ff1be12d 100755 --- a/akka-bbb-fsesl/src/main/scala/org/bigbluebutton/freeswitch/VoiceConferenceService.scala +++ b/akka-bbb-fsesl/src/main/scala/org/bigbluebutton/freeswitch/VoiceConferenceService.scala @@ -273,4 +273,41 @@ class VoiceConferenceService(sender: RedisPublisher) extends IVoiceConferenceSer sender.publish(fromVoiceConfRedisChannel, json) } + def voiceCallStateEvent( + conf: String, + callSession: String, + clientSession: String, + userId: String, + callerName: String, + callState: String, + origCallerIdName: String, + origCalledDest: String + ): Unit = { + val header = BbbCoreVoiceConfHeader(VoiceConfCallStateEvtMsg.NAME, conf) + val body = VoiceConfCallStateEvtMsgBody( + voiceConf = conf, + callSession = callSession, + clientSession = clientSession, + userId = userId, + callerName = callerName, + callState = callState, + origCallerIdName = origCallerIdName, + origCalledDest = origCalledDest + ) + val envelope = BbbCoreEnvelope(VoiceConfCallStateEvtMsg.NAME, Map("voiceConf" -> conf)) + + val msg = new VoiceConfCallStateEvtMsg(header, body) + val msgEvent = BbbCommonEnvCoreMsg(envelope, msg) + + val json = JsonUtil.toJson(msgEvent) + sender.publish(fromVoiceConfRedisChannel, json) + } + + def freeswitchStatusReplyEvent(json: String): Unit = { + // Placeholder so we can add a /healthz check endpoint to + // monitor akka-fsesl (ralam feb 5, 2020) + //println("***** >>>>") + //println(json) + //println("<<<< *****") + } } diff --git a/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/VoiceConfMsgs.scala b/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/VoiceConfMsgs.scala index 83f90ead4d..4d92363940 100755 --- a/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/VoiceConfMsgs.scala +++ b/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/VoiceConfMsgs.scala @@ -433,4 +433,40 @@ case class UserDisconnectedFromGlobalAudioMsgBody(userId: String, name: String) */ object SyncGetVoiceUsersRespMsg { val NAME = "SyncGetVoiceUsersRespMsg" } case class SyncGetVoiceUsersRespMsg(header: BbbClientMsgHeader, body: SyncGetVoiceUsersRespMsgBody) extends BbbCoreMsg -case class SyncGetVoiceUsersRespMsgBody(voiceUsers: Vector[VoiceConfUser]) \ No newline at end of file +case class SyncGetVoiceUsersRespMsgBody(voiceUsers: Vector[VoiceConfUser]) + +/** + * Received from FS call state events. + */ +object VoiceConfCallStateEvtMsg { val NAME = "VoiceConfCallStateEvtMsg" } +case class VoiceConfCallStateEvtMsg( + header: BbbCoreVoiceConfHeader, + body: VoiceConfCallStateEvtMsgBody +) extends VoiceStandardMsg +case class VoiceConfCallStateEvtMsgBody( + voiceConf: String, + callSession: String, + clientSession: String, + userId: String, + callerName: String, + callState: String, + origCallerIdName: String, + origCalledDest: String +) + +/** + * Sent to interested parties call state events. + */ +object VoiceCallStateEvtMsg { val NAME = "VoiceCallStateEvtMsg" } +case class VoiceCallStateEvtMsg( + header: BbbClientMsgHeader, + body: VoiceCallStateEvtMsgBody +) extends BbbCoreMsg +case class VoiceCallStateEvtMsgBody( + meetingId: String, + voiceConf: String, + clientSession: String, + userId: String, + callerName: String, + callState: String +) \ No newline at end of file diff --git a/bbb-fsesl-client/src/main/java/org/freeswitch/esl/client/inbound/Client.java b/bbb-fsesl-client/src/main/java/org/freeswitch/esl/client/inbound/Client.java index 482067ed8e..dc5d0f6d46 100755 --- a/bbb-fsesl-client/src/main/java/org/freeswitch/esl/client/inbound/Client.java +++ b/bbb-fsesl-client/src/main/java/org/freeswitch/esl/client/inbound/Client.java @@ -428,6 +428,7 @@ public class Client public void eventReceived( final EslEvent event ) { log.debug( "Event received [{}]", event ); + /* * Notify listeners in a different thread in order to: * - not to block the IO threads with potentially long-running listeners @@ -537,8 +538,10 @@ public class Client System.out.println("##### " + sb.toString()); **/ } + } else { + listener.eventReceived( event ); } - listener.eventReceived( event ); + } catch ( Throwable t ) { log.error( "Error caught notifying listener of event [" + event + ']', t ); } -- GitLab