diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/SystemConfiguration.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/SystemConfiguration.scala index ffc94b6bab..6cf879b890 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/SystemConfiguration.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/SystemConfiguration.scala @@ -40,6 +40,7 @@ trait SystemConfiguration { lazy val checkVoiceRecordingInterval = Try(config.getInt("voiceConf.checkRecordingInterval")).getOrElse(19) lazy val syncVoiceUsersStatusInterval = Try(config.getInt("voiceConf.syncUserStatusInterval")).getOrElse(43) lazy val ejectRogueVoiceUsers = Try(config.getBoolean("voiceConf.ejectRogueVoiceUsers")).getOrElse(true) + lazy val dialInApprovalAudioPath = Try(config.getString("voiceConf.dialInApprovalAudioPath")).getOrElse("ivr/ivr-please_hold_while_party_contacted.wav") lazy val recordingChapterBreakLengthInMinutes = Try(config.getInt("recording.chapterBreakLengthInMinutes")).getOrElse(0) diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/voice/UserJoinedVoiceConfEvtMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/voice/UserJoinedVoiceConfEvtMsgHdlr.scala index 221db4e39a..5ea211ffec 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/voice/UserJoinedVoiceConfEvtMsgHdlr.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/voice/UserJoinedVoiceConfEvtMsgHdlr.scala @@ -63,6 +63,13 @@ trait UserJoinedVoiceConfEvtMsgHdlr extends SystemConfiguration { val guest = GuestWaiting(msg.body.intId, msg.body.callerIdName, Roles.VIEWER_ROLE, true, "", true, System.currentTimeMillis()) GuestsWaiting.add(liveMeeting.guestsWaiting, guest) notifyModeratorsOfGuestWaiting(guest, liveMeeting.users2x, liveMeeting.props.meetingProp.intId) + + VoiceApp.toggleUserAudioInVoiceConf( + liveMeeting, + outGW, + msg.body.voiceUserId, + false + ) } } diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/voice/VoiceApp.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/voice/VoiceApp.scala index 54146a56e5..776731fe41 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/voice/VoiceApp.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/voice/VoiceApp.scala @@ -353,4 +353,66 @@ object VoiceApp extends SystemConfiguration { ) } } + +/** Toggle audio for the given user in voice conference. + * + * We first stop the current audio being played, preventing the playback + * to also mix the "You are the first person ..." audio. + * After that we check if we are turning on/off based on enabled param. If + * enabled is false we: + * - play a sound to let user know that an action is required + * (eg. guest approval) from the server/room. + * - put the user on hold, so DTMFs for mute / deaf mute are also disabled + * - mute the user (other participants won't hear users's audio) + * - deaf the user (user won't hear other participant's audio) + * If disabled, we remove user from hold, mute and deaf states, allowing the + * user to interact with the room. + */ + def toggleUserAudioInVoiceConf( + liveMeeting: LiveMeeting, + outGW: OutMsgRouter, + voiceUserId: String, + enabled: Boolean + ): Unit = { + val stopEvent = MsgBuilder.buildStopSoundInVoiceConfSysMsg( + liveMeeting.props.meetingProp.intId, + liveMeeting.props.voiceProp.voiceConf, + "" + ) + outGW.send(stopEvent) + + if (!enabled) { + val playEvent = MsgBuilder.buildPlaySoundInVoiceConfSysMsg( + liveMeeting.props.meetingProp.intId, + liveMeeting.props.voiceProp.voiceConf, + voiceUserId, + dialInApprovalAudioPath + ) + outGW.send(playEvent) + } + + val holdEvent = MsgBuilder.buildHoldUserInVoiceConfSysMsg( + liveMeeting.props.meetingProp.intId, + liveMeeting.props.voiceProp.voiceConf, + voiceUserId, + !enabled + ) + outGW.send(holdEvent) + + val muteEvent = MsgBuilder.buildMuteUserInVoiceConfSysMsg( + liveMeeting.props.meetingProp.intId, + liveMeeting.props.voiceProp.voiceConf, + voiceUserId, + !enabled + ) + outGW.send(muteEvent) + + val deafEvent = MsgBuilder.buildDeafUserInVoiceConfSysMsg( + liveMeeting.props.meetingProp.intId, + liveMeeting.props.voiceProp.voiceConf, + voiceUserId, + !enabled + ) + outGW.send(deafEvent) + } } 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 126a99f0a5..ca6bc9f171 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 @@ -65,6 +65,10 @@ class AnalyticsActor(val includeChat: Boolean) extends Actor with ActorLogging { case m: RecordingStartedVoiceConfEvtMsg => logMessage(msg) case m: MuteUserCmdMsg => logMessage(msg) case m: MuteUserInVoiceConfSysMsg => logMessage(msg) + case m: DeafUserInVoiceConfSysMsg => logMessage(msg) + case m: HoldUserInVoiceConfSysMsg => logMessage(msg) + case m: PlaySoundInVoiceConfSysMsg => logMessage(msg) + case m: StopSoundInVoiceConfSysMsg => logMessage(msg) case m: MuteAllExceptPresentersCmdMsg => logMessage(msg) case m: EjectUserFromVoiceCmdMsg => logMessage(msg) case m: MuteMeetingCmdMsg => logMessage(msg) diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/FromAkkaAppsMsgSenderActor.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/FromAkkaAppsMsgSenderActor.scala index 9d46810e4a..15a5fbead9 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/FromAkkaAppsMsgSenderActor.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/FromAkkaAppsMsgSenderActor.scala @@ -49,6 +49,14 @@ class FromAkkaAppsMsgSenderActor(msgSender: MessageSender) msgSender.send(toVoiceConfRedisChannel, json) case MuteUserInVoiceConfSysMsg.NAME => msgSender.send(toVoiceConfRedisChannel, json) + case DeafUserInVoiceConfSysMsg.NAME => + msgSender.send(toVoiceConfRedisChannel, json) + case HoldUserInVoiceConfSysMsg.NAME => + msgSender.send(toVoiceConfRedisChannel, json) + case PlaySoundInVoiceConfSysMsg.NAME => + msgSender.send(toVoiceConfRedisChannel, json) + case StopSoundInVoiceConfSysMsg.NAME => + msgSender.send(toVoiceConfRedisChannel, json) case StartRecordingVoiceConfSysMsg.NAME => msgSender.send(toVoiceConfRedisChannel, json) case StopRecordingVoiceConfSysMsg.NAME => diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/guests/GuestsWaitingApprovedMsgHdlr.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/guests/GuestsWaitingApprovedMsgHdlr.scala index ceb53eb1de..afebbae7ce 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/guests/GuestsWaitingApprovedMsgHdlr.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/handlers/guests/GuestsWaitingApprovedMsgHdlr.scala @@ -45,6 +45,23 @@ trait GuestsWaitingApprovedMsgHdlr extends HandlerHelpers with RightsManagementT false, "freeswitch" ) + VoiceUsers.findWithIntId( + liveMeeting.voiceUsers, + dialInUser.intId + ) match { + case Some(vu) => + VoiceApp.toggleUserAudioInVoiceConf( + liveMeeting, + outGW, + vu.voiceUserId, + true + ) + case None => + println(s"Skipping transferring dial-in user to the " + + "voiceconf: dial-in user already left. meetingId= " + + "${liveMeeting.props.meetingProp.intId}, userId=" + + "${dialInUser.intId}") + } } else { VoiceApp.removeUserFromVoiceConf(liveMeeting, outGW, dialInUser.extId) val event = MsgBuilder.buildEjectUserFromVoiceConfSysMsg(liveMeeting.props.meetingProp.intId, liveMeeting.props.voiceProp.voiceConf, g.guest) diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/senders/MsgBuilder.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/senders/MsgBuilder.scala index 8b4ce3d073..0b1db206b5 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/senders/MsgBuilder.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core2/message/senders/MsgBuilder.scala @@ -336,6 +336,46 @@ object MsgBuilder { BbbCommonEnvCoreMsg(envelope, event) } + def buildDeafUserInVoiceConfSysMsg(meetingId: String, voiceConf: String, voiceUserId: String, deaf: Boolean): BbbCommonEnvCoreMsg = { + val routing = collection.immutable.HashMap("sender" -> "bbb-apps-akka") + val envelope = BbbCoreEnvelope(DeafUserInVoiceConfSysMsg.NAME, routing) + val body = DeafUserInVoiceConfSysMsgBody(voiceConf, voiceUserId, deaf) + val header = BbbCoreHeaderWithMeetingId(DeafUserInVoiceConfSysMsg.NAME, meetingId) + val event = DeafUserInVoiceConfSysMsg(header, body) + + BbbCommonEnvCoreMsg(envelope, event) + } + + def buildHoldUserInVoiceConfSysMsg(meetingId: String, voiceConf: String, voiceUserId: String, hold: Boolean): BbbCommonEnvCoreMsg = { + val routing = collection.immutable.HashMap("sender" -> "bbb-apps-akka") + val envelope = BbbCoreEnvelope(HoldUserInVoiceConfSysMsg.NAME, routing) + val body = HoldUserInVoiceConfSysMsgBody(voiceConf, voiceUserId, hold) + val header = BbbCoreHeaderWithMeetingId(HoldUserInVoiceConfSysMsg.NAME, meetingId) + val event = HoldUserInVoiceConfSysMsg(header, body) + + BbbCommonEnvCoreMsg(envelope, event) + } + + def buildPlaySoundInVoiceConfSysMsg(meetingId: String, voiceConf: String, voiceUserId: String, soundPath: String): BbbCommonEnvCoreMsg = { + val routing = collection.immutable.HashMap("sender" -> "bbb-apps-akka") + val envelope = BbbCoreEnvelope(PlaySoundInVoiceConfSysMsg.NAME, routing) + val body = PlaySoundInVoiceConfSysMsgBody(voiceConf, voiceUserId, soundPath) + val header = BbbCoreHeaderWithMeetingId(PlaySoundInVoiceConfSysMsg.NAME, meetingId) + val event = PlaySoundInVoiceConfSysMsg(header, body) + + BbbCommonEnvCoreMsg(envelope, event) + } + + def buildStopSoundInVoiceConfSysMsg(meetingId: String, voiceConf: String, voiceUserId: String): BbbCommonEnvCoreMsg = { + val routing = collection.immutable.HashMap("sender" -> "bbb-apps-akka") + val envelope = BbbCoreEnvelope(StopSoundInVoiceConfSysMsg.NAME, routing) + val body = StopSoundInVoiceConfSysMsgBody(voiceConf, voiceUserId) + val header = BbbCoreHeaderWithMeetingId(StopSoundInVoiceConfSysMsg.NAME, meetingId) + val event = StopSoundInVoiceConfSysMsg(header, body) + + BbbCommonEnvCoreMsg(envelope, event) + } + def buildBreakoutRoomEndedEvtMsg(meetingId: String, userId: String, breakoutRoomId: String): BbbCommonEnvCoreMsg = { val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, userId) val envelope = BbbCoreEnvelope(BreakoutRoomEndedEvtMsg.NAME, routing) diff --git a/akka-bbb-apps/src/universal/conf/application.conf b/akka-bbb-apps/src/universal/conf/application.conf index f44cfd6062..60ee61d713 100755 --- a/akka-bbb-apps/src/universal/conf/application.conf +++ b/akka-bbb-apps/src/universal/conf/application.conf @@ -88,6 +88,10 @@ voiceConf { syncUserStatusInterval = 41 # Voice users with no matching user record ejectRogueVoiceUsers = true + + # Path to the audio file being played when dial-in user is waiting for + # approval. This can be relative to FreeSWITCH sounds folder + dialInApprovalAudioPath = "ivr/ivr-please_hold_while_party_contacted.wav" } recording { 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 d87f80422e..f66a2e3a69 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 @@ -204,6 +204,34 @@ public class ConnectionManager { } } + public void deaf(DeafUserCommand duc) { + Client c = manager.getESLClient(); + if (c.canSend()) { + c.sendAsyncApiCommand(duc.getCommand(), duc.getCommandArgs()); + } + } + + public void hold(HoldUserCommand huc) { + Client c = manager.getESLClient(); + if (c.canSend()) { + c.sendAsyncApiCommand(huc.getCommand(), huc.getCommandArgs()); + } + } + + public void playSound(PlaySoundCommand psc) { + Client c = manager.getESLClient(); + if (c.canSend()) { + c.sendAsyncApiCommand(psc.getCommand(), psc.getCommandArgs()); + } + } + + public void stopSound(StopSoundCommand ssc) { + Client c = manager.getESLClient(); + if (c.canSend()) { + c.sendAsyncApiCommand(ssc.getCommand(), ssc.getCommandArgs()); + } + } + public void tranfer(TransferUserToMeetingCommand tutmc) { Client c = manager.getESLClient(); if (c.canSend()) { diff --git a/akka-bbb-fsesl/src/main/java/org/bigbluebutton/freeswitch/voice/freeswitch/FreeswitchApplication.java b/akka-bbb-fsesl/src/main/java/org/bigbluebutton/freeswitch/voice/freeswitch/FreeswitchApplication.java index 90fbcd590c..9acdcc5672 100755 --- a/akka-bbb-fsesl/src/main/java/org/bigbluebutton/freeswitch/voice/freeswitch/FreeswitchApplication.java +++ b/akka-bbb-fsesl/src/main/java/org/bigbluebutton/freeswitch/voice/freeswitch/FreeswitchApplication.java @@ -28,6 +28,10 @@ import org.bigbluebutton.freeswitch.voice.freeswitch.actions.EjectUserCommand; import org.bigbluebutton.freeswitch.voice.freeswitch.actions.FreeswitchCommand; import org.bigbluebutton.freeswitch.voice.freeswitch.actions.GetAllUsersCommand; import org.bigbluebutton.freeswitch.voice.freeswitch.actions.MuteUserCommand; +import org.bigbluebutton.freeswitch.voice.freeswitch.actions.DeafUserCommand; +import org.bigbluebutton.freeswitch.voice.freeswitch.actions.HoldUserCommand; +import org.bigbluebutton.freeswitch.voice.freeswitch.actions.PlaySoundCommand; +import org.bigbluebutton.freeswitch.voice.freeswitch.actions.StopSoundCommand; import org.bigbluebutton.freeswitch.voice.freeswitch.actions.RecordConferenceCommand; import org.bigbluebutton.freeswitch.voice.freeswitch.actions.TransferUserToMeetingCommand; import org.bigbluebutton.freeswitch.voice.freeswitch.actions.*; @@ -123,6 +127,26 @@ public class FreeswitchApplication implements IDelayedCommandListener{ queueMessage(mpc); } + public void deafUser(String voiceConfId, String voiceUserId, Boolean deaf) { + DeafUserCommand duc = new DeafUserCommand(voiceConfId, voiceUserId, deaf, USER); + queueMessage(duc); + } + + public void holdUser(String voiceConfId, String voiceUserId, Boolean hold) { + HoldUserCommand huc = new HoldUserCommand(voiceConfId, voiceUserId, hold, USER); + queueMessage(huc); + } + + public void playSound(String voiceConfId, String voiceUserId, String soundPath) { + PlaySoundCommand psc = new PlaySoundCommand(voiceConfId, voiceUserId, soundPath, USER); + queueMessage(psc); + } + + public void stopSound(String voiceConfId, String voiceUserId) { + StopSoundCommand ssc = new StopSoundCommand(voiceConfId, voiceUserId, USER); + queueMessage(ssc); + } + public void eject(String voiceConfId, String voiceUserId) { EjectUserCommand mpc = new EjectUserCommand(voiceConfId, voiceUserId, USER); queueMessage(mpc); @@ -158,6 +182,18 @@ public class FreeswitchApplication implements IDelayedCommandListener{ } else if (command instanceof MuteUserCommand) { MuteUserCommand cmd = (MuteUserCommand) command; manager.mute(cmd); + } else if (command instanceof DeafUserCommand) { + DeafUserCommand cmd = (DeafUserCommand) command; + manager.deaf(cmd); + } else if (command instanceof HoldUserCommand) { + HoldUserCommand cmd = (HoldUserCommand) command; + manager.hold(cmd); + } else if (command instanceof PlaySoundCommand) { + PlaySoundCommand cmd = (PlaySoundCommand) command; + manager.playSound(cmd); + } else if (command instanceof StopSoundCommand) { + StopSoundCommand cmd = (StopSoundCommand) command; + manager.stopSound(cmd); } else if (command instanceof EjectUserCommand) { EjectUserCommand cmd = (EjectUserCommand) command; manager.eject(cmd); diff --git a/akka-bbb-fsesl/src/main/java/org/bigbluebutton/freeswitch/voice/freeswitch/actions/DeafUserCommand.java b/akka-bbb-fsesl/src/main/java/org/bigbluebutton/freeswitch/voice/freeswitch/actions/DeafUserCommand.java new file mode 100644 index 0000000000..12c67136bc --- /dev/null +++ b/akka-bbb-fsesl/src/main/java/org/bigbluebutton/freeswitch/voice/freeswitch/actions/DeafUserCommand.java @@ -0,0 +1,40 @@ +/** +* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/ +* +* Copyright (c) 2022 BigBlueButton Inc. and by respective authors (see below). +* +* This program is free software; you can redistribute it and/or modify it under the +* terms of the GNU Lesser General Public License as published by the Free Software +* Foundation; either version 3.0 of the License, or (at your option) any later +* version. +* +* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY +* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License along +* with BigBlueButton; if not, see . +* +*/ +package org.bigbluebutton.freeswitch.voice.freeswitch.actions; + +public class DeafUserCommand extends FreeswitchCommand { + + private final String participant; + private final Boolean deaf; + + public DeafUserCommand(String room, String participant, Boolean deaf, String requesterId) { + super(room, requesterId); + this.participant = participant; + this.deaf = deaf; + } + + @Override + public String getCommandArgs() { + String action = "undeaf"; + if (deaf) action = "deaf"; + + return room + SPACE + action + SPACE + participant; + } + +} diff --git a/akka-bbb-fsesl/src/main/java/org/bigbluebutton/freeswitch/voice/freeswitch/actions/HoldUserCommand.java b/akka-bbb-fsesl/src/main/java/org/bigbluebutton/freeswitch/voice/freeswitch/actions/HoldUserCommand.java new file mode 100644 index 0000000000..43ec940327 --- /dev/null +++ b/akka-bbb-fsesl/src/main/java/org/bigbluebutton/freeswitch/voice/freeswitch/actions/HoldUserCommand.java @@ -0,0 +1,40 @@ +/** +* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/ +* +* Copyright (c) 2022 BigBlueButton Inc. and by respective authors (see below). +* +* This program is free software; you can redistribute it and/or modify it under the +* terms of the GNU Lesser General Public License as published by the Free Software +* Foundation; either version 3.0 of the License, or (at your option) any later +* version. +* +* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY +* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License along +* with BigBlueButton; if not, see . +* +*/ +package org.bigbluebutton.freeswitch.voice.freeswitch.actions; + +public class HoldUserCommand extends FreeswitchCommand { + + private final String participant; + private final Boolean hold; + + public HoldUserCommand(String room, String participant, Boolean hold, String requesterId) { + super(room, requesterId); + this.participant = participant; + this.hold = hold; + } + + @Override + public String getCommandArgs() { + String action = "unhold"; + if (hold) action = "hold"; + + return room + SPACE + action + SPACE + participant; + } + +} diff --git a/akka-bbb-fsesl/src/main/java/org/bigbluebutton/freeswitch/voice/freeswitch/actions/PlaySoundCommand.java b/akka-bbb-fsesl/src/main/java/org/bigbluebutton/freeswitch/voice/freeswitch/actions/PlaySoundCommand.java new file mode 100644 index 0000000000..b4aeba0e36 --- /dev/null +++ b/akka-bbb-fsesl/src/main/java/org/bigbluebutton/freeswitch/voice/freeswitch/actions/PlaySoundCommand.java @@ -0,0 +1,40 @@ +/** +* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/ +* +* Copyright (c) 2022 BigBlueButton Inc. and by respective authors (see below). +* +* This program is free software; you can redistribute it and/or modify it under the +* terms of the GNU Lesser General Public License as published by the Free Software +* Foundation; either version 3.0 of the License, or (at your option) any later +* version. +* +* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY +* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License along +* with BigBlueButton; if not, see . +* +*/ +package org.bigbluebutton.freeswitch.voice.freeswitch.actions; + +public class PlaySoundCommand extends FreeswitchCommand { + + private final String participant; + private final String soundPath; + + public PlaySoundCommand(String room, String participant, String soundPath, String requesterId) { + super(room, requesterId); + this.participant = participant; + this.soundPath = soundPath; + } + + @Override + public String getCommandArgs() { + String action = "play"; + + return room + SPACE + action + SPACE + + this.soundPath + SPACE + participant; + } + +} diff --git a/akka-bbb-fsesl/src/main/java/org/bigbluebutton/freeswitch/voice/freeswitch/actions/StopSoundCommand.java b/akka-bbb-fsesl/src/main/java/org/bigbluebutton/freeswitch/voice/freeswitch/actions/StopSoundCommand.java new file mode 100644 index 0000000000..0c96fd310c --- /dev/null +++ b/akka-bbb-fsesl/src/main/java/org/bigbluebutton/freeswitch/voice/freeswitch/actions/StopSoundCommand.java @@ -0,0 +1,37 @@ +/** +* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/ +* +* Copyright (c) 2022 BigBlueButton Inc. and by respective authors (see below). +* +* This program is free software; you can redistribute it and/or modify it under the +* terms of the GNU Lesser General Public License as published by the Free Software +* Foundation; either version 3.0 of the License, or (at your option) any later +* version. +* +* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY +* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public License along +* with BigBlueButton; if not, see . +* +*/ +package org.bigbluebutton.freeswitch.voice.freeswitch.actions; + +public class StopSoundCommand extends FreeswitchCommand { + + private final String participant; + + public StopSoundCommand(String room, String participant, String requesterId) { + super(room, requesterId); + this.participant = participant; + } + + @Override + public String getCommandArgs() { + String action = "stop all"; + + return room + SPACE + action + SPACE + participant; + } + +} diff --git a/akka-bbb-fsesl/src/main/scala/org/bigbluebutton/freeswitch/RxJsonMsgDeserializer.scala b/akka-bbb-fsesl/src/main/scala/org/bigbluebutton/freeswitch/RxJsonMsgDeserializer.scala index 5df337fb31..c6557a5317 100755 --- a/akka-bbb-fsesl/src/main/scala/org/bigbluebutton/freeswitch/RxJsonMsgDeserializer.scala +++ b/akka-bbb-fsesl/src/main/scala/org/bigbluebutton/freeswitch/RxJsonMsgDeserializer.scala @@ -117,6 +117,78 @@ trait RxJsonMsgDeserializer { } } + def routeDeafUserInVoiceConfMsg(envelope: BbbCoreEnvelope, jsonNode: JsonNode): Unit = { + def deserialize(jsonNode: JsonNode): Option[DeafUserInVoiceConfSysMsg] = { + val (result, error) = JsonDeserializer.toBbbCommonMsg[DeafUserInVoiceConfSysMsg](jsonNode) + result match { + case Some(msg) => Some(msg.asInstanceOf[DeafUserInVoiceConfSysMsg]) + case None => + log.error("Failed to deserialize message: error: {} \n msg: {}", error, jsonNode) + None + } + } + + for { + m <- deserialize(jsonNode) + } yield { + fsApp.deafUser(m.body.voiceConf, m.body.voiceUserId, m.body.deaf) + } + } + + def routeHoldUserInVoiceConfMsg(envelope: BbbCoreEnvelope, jsonNode: JsonNode): Unit = { + def deserialize(jsonNode: JsonNode): Option[HoldUserInVoiceConfSysMsg] = { + val (result, error) = JsonDeserializer.toBbbCommonMsg[HoldUserInVoiceConfSysMsg](jsonNode) + result match { + case Some(msg) => Some(msg.asInstanceOf[HoldUserInVoiceConfSysMsg]) + case None => + log.error("Failed to deserialize message: error: {} \n msg: {}", error, jsonNode) + None + } + } + + for { + m <- deserialize(jsonNode) + } yield { + fsApp.holdUser(m.body.voiceConf, m.body.voiceUserId, m.body.hold) + } + } + + def routePlaySoundInVoiceConfMsg(envelope: BbbCoreEnvelope, jsonNode: JsonNode): Unit = { + def deserialize(jsonNode: JsonNode): Option[PlaySoundInVoiceConfSysMsg] = { + val (result, error) = JsonDeserializer.toBbbCommonMsg[PlaySoundInVoiceConfSysMsg](jsonNode) + result match { + case Some(msg) => Some(msg.asInstanceOf[PlaySoundInVoiceConfSysMsg]) + case None => + log.error("Failed to deserialize message: error: {} \n msg: {}", error, jsonNode) + None + } + } + + for { + m <- deserialize(jsonNode) + } yield { + fsApp.playSound(m.body.voiceConf, m.body.voiceUserId, m.body.soundPath) + } + } + + def routeStopSoundInVoiceConfMsg(envelope: BbbCoreEnvelope, jsonNode: JsonNode): Unit = { + def deserialize(jsonNode: JsonNode): Option[StopSoundInVoiceConfSysMsg] = { + val (result, error) = JsonDeserializer.toBbbCommonMsg[StopSoundInVoiceConfSysMsg](jsonNode) + result match { + case Some(msg) => Some(msg.asInstanceOf[StopSoundInVoiceConfSysMsg]) + case None => + log.error("Failed to deserialize message: error: {} \n msg: {}", error, jsonNode) + None + } + } + + for { + m <- deserialize(jsonNode) + } yield { + fsApp.stopSound(m.body.voiceConf, m.body.voiceUserId) + } + } + def routeTransferUserToVoiceConfMsg(envelope: BbbCoreEnvelope, jsonNode: JsonNode): Unit = { def deserialize(jsonNode: JsonNode): Option[TransferUserToVoiceConfSysMsg] = { val (result, error) = JsonDeserializer.toBbbCommonMsg[TransferUserToVoiceConfSysMsg](jsonNode) diff --git a/akka-bbb-fsesl/src/main/scala/org/bigbluebutton/freeswitch/RxJsonMsgHdlrActor.scala b/akka-bbb-fsesl/src/main/scala/org/bigbluebutton/freeswitch/RxJsonMsgHdlrActor.scala index 0d82974ffd..51a812eca0 100755 --- a/akka-bbb-fsesl/src/main/scala/org/bigbluebutton/freeswitch/RxJsonMsgHdlrActor.scala +++ b/akka-bbb-fsesl/src/main/scala/org/bigbluebutton/freeswitch/RxJsonMsgHdlrActor.scala @@ -42,6 +42,14 @@ class RxJsonMsgHdlrActor(val fsApp: FreeswitchApplication) extends Actor with Ac routeEjectUserFromVoiceConfMsg(envelope, jsonNode) case MuteUserInVoiceConfSysMsg.NAME => routeMuteUserInVoiceConfMsg(envelope, jsonNode) + case DeafUserInVoiceConfSysMsg.NAME => + routeDeafUserInVoiceConfMsg(envelope, jsonNode) + case HoldUserInVoiceConfSysMsg.NAME => + routeHoldUserInVoiceConfMsg(envelope, jsonNode) + case PlaySoundInVoiceConfSysMsg.NAME => + routePlaySoundInVoiceConfMsg(envelope, jsonNode) + case StopSoundInVoiceConfSysMsg.NAME => + routeStopSoundInVoiceConfMsg(envelope, jsonNode) case TransferUserToVoiceConfSysMsg.NAME => routeTransferUserToVoiceConfMsg(envelope, jsonNode) case StartRecordingVoiceConfSysMsg.NAME => 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 87aabea605..7abc4127c5 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 @@ -258,6 +258,46 @@ case class MeetingMutedEvtMsg( ) extends BbbCoreMsg case class MeetingMutedEvtMsgBody(muted: Boolean, mutedBy: String) +/** + * Send to FS to deaf user in the voice conference. + */ +object DeafUserInVoiceConfSysMsg { val NAME = "DeafUserInVoiceConfSysMsg" } +case class DeafUserInVoiceConfSysMsg( + header: BbbCoreHeaderWithMeetingId, + body: DeafUserInVoiceConfSysMsgBody +) extends BbbCoreMsg +case class DeafUserInVoiceConfSysMsgBody(voiceConf: String, voiceUserId: String, deaf: Boolean) + +/** + * Send to FS to hold user in the voice conference. + */ +object HoldUserInVoiceConfSysMsg { val NAME = "HoldUserInVoiceConfSysMsg" } +case class HoldUserInVoiceConfSysMsg( + header: BbbCoreHeaderWithMeetingId, + body: HoldUserInVoiceConfSysMsgBody +) extends BbbCoreMsg +case class HoldUserInVoiceConfSysMsgBody(voiceConf: String, voiceUserId: String, hold: Boolean) + +/** + * Send to FS to play sound in the voice conference, or specific user + */ +object PlaySoundInVoiceConfSysMsg { val NAME = "PlaySoundInVoiceConfSysMsg" } +case class PlaySoundInVoiceConfSysMsg( + header: BbbCoreHeaderWithMeetingId, + body: PlaySoundInVoiceConfSysMsgBody +) extends BbbCoreMsg +case class PlaySoundInVoiceConfSysMsgBody(voiceConf: String, voiceUserId: String, soundPath: String) + +/** + * Send to FS to stop current sound in the voice conference, or specific user + */ +object StopSoundInVoiceConfSysMsg { val NAME = "StopSoundInVoiceConfSysMsg" } +case class StopSoundInVoiceConfSysMsg( + header: BbbCoreHeaderWithMeetingId, + body: StopSoundInVoiceConfSysMsgBody +) extends BbbCoreMsg +case class StopSoundInVoiceConfSysMsgBody(voiceConf: String, voiceUserId: String) + /** * Received from FS that voice is being recorded. */