From af528b26d12effd509cc85c499f89a8ff467e10a Mon Sep 17 00:00:00 2001 From: Aron Engineer Date: Thu, 18 Mar 2021 21:43:49 +0000 Subject: [PATCH 01/14] feat: WIP meeting info analytics logging messages computed --- .../core/running/MeetingActor.scala | 73 ++++++++++++++++--- .../bigbluebutton/core2/AnalyticsActor.scala | 3 +- .../core2/message/senders/MsgBuilder.scala | 10 +++ .../msgs/MeetingInfoAnalyticsMessages.scala | 50 +++++++++++++ 4 files changed, 126 insertions(+), 10 deletions(-) create mode 100755 bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/MeetingInfoAnalyticsMessages.scala 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 e72b3f4bf8..152d2f4976 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 @@ -1,9 +1,6 @@ package org.bigbluebutton.core.running import java.io.{ PrintWriter, StringWriter } - -import akka.actor._ -import akka.actor.SupervisorStrategy.Resume import org.bigbluebutton.SystemConfiguration import org.bigbluebutton.core.apps.groupchats.GroupChatHdlrs import org.bigbluebutton.core.apps.presentationpod._ @@ -22,7 +19,7 @@ import org.bigbluebutton.core.apps.presentation.PresentationApp2x import org.bigbluebutton.core.apps.users.UsersApp2x import org.bigbluebutton.core.apps.whiteboard.WhiteboardApp2x import org.bigbluebutton.core.bus._ -import org.bigbluebutton.core.models._ +import org.bigbluebutton.core.models.{ VoiceUsers, _ } import org.bigbluebutton.core2.{ MeetingStatus2x, Permissions } import org.bigbluebutton.core2.message.handlers._ import org.bigbluebutton.core2.message.handlers.meeting._ @@ -31,13 +28,18 @@ import org.bigbluebutton.common2.msgs._ import org.bigbluebutton.core.apps.breakout._ import org.bigbluebutton.core.apps.polls._ import org.bigbluebutton.core.apps.voice._ -import akka.actor._ +import akka.actor.Props +import akka.actor.OneForOneStrategy import akka.actor.SupervisorStrategy.Resume +import org.bigbluebutton.common2.msgs import scala.concurrent.duration._ import org.bigbluebutton.core.apps.layout.LayoutApp2x import org.bigbluebutton.core.apps.meeting.{ SyncGetMeetingInfoRespMsgHdlr, ValidateConnAuthTokenSysMsgHdlr } import org.bigbluebutton.core.apps.users.ChangeLockSettingsInMeetingCmdMsgHdlr +import org.bigbluebutton.core.models.VoiceUsers.findAllNonListenOnlyVoiceUsers +import org.bigbluebutton.core.models.Webcams.findAll +import org.bigbluebutton.core2.MeetingStatus2x.{ hasAuthedUserJoined, isVoiceRecording } import org.bigbluebutton.core2.message.senders.{ MsgBuilder, Sender } import scala.concurrent.ExecutionContext.Implicits.global @@ -96,6 +98,7 @@ class MeetingActor( object CheckVoiceRecordingInternalMsg object SyncVoiceUserStatusInternalMsg + object MeetingInfoAnalyticsMsg override val supervisorStrategy = OneForOneStrategy(maxNrOfRetries = 10, withinTimeRange = 1 minute) { case e: Exception => { @@ -207,12 +210,20 @@ class MeetingActor( CheckVoiceRecordingInternalMsg ) + context.system.scheduler.schedule( + 5 seconds, + 5 second, + self, + MeetingInfoAnalyticsMsg + ) + def receive = { case SyncVoiceUserStatusInternalMsg => checkVoiceConfUsersStatus() case CheckVoiceRecordingInternalMsg => checkVoiceConfIsRunningAndRecording() - + case MeetingInfoAnalyticsMsg => + handleMeetingInfoAnalyticsLogging() //============================= // 2x messages @@ -498,6 +509,52 @@ class MeetingActor( } } + def handleMeetingInfoAnalyticsLogging(): Unit = { + println("***********Reaches here************") + val meetingName: String = liveMeeting.props.meetingProp.name + val externalId: String = liveMeeting.props.meetingProp.extId + val internalId: String = liveMeeting.props.meetingProp.intId + val hasUserJoined: Boolean = hasAuthedUserJoined(liveMeeting.status) + val isRecording: Boolean = isVoiceRecording(liveMeeting.status) + + val liveWebcams: Vector[WebcamStream] = findAll(liveMeeting.webcams) + val numOfLiveWebcams: Int = liveWebcams.length + val mediaStreamLiveWebcamUserId: String = liveWebcams.flatMap(_.stream.userId).mkString + val viewers: List[String] = findAll(liveMeeting.webcams).flatMap(_.stream.viewers).toList + + val webcamDetail: WebcamDetail = WebcamDetail(mediaStreamLiveWebcamUserId, viewers) + val webcam: Webcam = Webcam(numOfLiveWebcams, webcamDetail) + + val voiceUsers: Vector[VoiceUserState] = VoiceUsers.findAll(liveMeeting.voiceUsers) + val numOfVoiceUsers: Int = voiceUsers.length + val numOfListenOnlyUsers: Int = numOfVoiceUsers - findAllNonListenOnlyVoiceUsers(liveMeeting.voiceUsers).length + val listeners: List[String] = voiceUsers.map(_.voiceUserId).toList + + val audio: Audio = Audio(numOfVoiceUsers, numOfListenOnlyUsers, listeners) + + val screenshareConfName: String = liveMeeting.props.screenshareProps.screenshareConf + val screenshare: Screenshare = Screenshare(screenshareConfName) + + val listOfUsers: List[String] = Users2x.findAll(liveMeeting.users2x).map(_.name).toList + + val presentation: msgs.Presentation = msgs.Presentation("test", "test") + + val breakoutRoom: BreakoutRoom = BreakoutRoom( + liveMeeting.props.breakoutProps.parentId, + liveMeeting.props.breakoutProps.breakoutRooms.toList + ) + + val meetingInfoAnalyticsLogMessage: MeetingInfoAnalytics = MeetingInfoAnalytics(meetingName, externalId, internalId, + hasUserJoined, isRecording, numOfLiveWebcams, numOfVoiceUsers, + webcam, audio, screenshare, listOfUsers, presentation, breakoutRoom) + + val event = MsgBuilder.buildMeetingInfoAnalyticsMsg( + meetingInfoAnalyticsLogMessage + ) + + outGW.send(event) + } + def handleGetRunningMeetingStateReqMsg(msg: GetRunningMeetingStateReqMsg): Unit = { processGetRunningMeetingStateReqMsg() } @@ -662,9 +719,7 @@ class MeetingActor( } } - def handleExtendMeetingDuration(msg: ExtendMeetingDuration) { - - } + def handleExtendMeetingDuration(msg: ExtendMeetingDuration) = ??? def removeUsersWithExpiredUserLeftFlag(liveMeeting: LiveMeeting, state: MeetingState2x): MeetingState2x = { val leftUsers = Users2x.findAllExpiredUserLeftFlags(liveMeeting.users2x, expiryTracker.meetingExpireWhenLastUserLeftInMs) 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 608f9c2d84..a79abad4f5 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 @@ -3,7 +3,7 @@ package org.bigbluebutton.core2 import akka.actor.{ Actor, ActorLogging, Props } import org.bigbluebutton.common2.msgs._ import org.bigbluebutton.common2.util.JsonUtil - +import org.bigbluebutton.common2.msgs.MeetingInfoAnalytics object AnalyticsActor { def props(): Props = Props(classOf[AnalyticsActor]) } @@ -159,6 +159,7 @@ class AnalyticsActor extends Actor with ActorLogging { case m: ChangeLockSettingsInMeetingCmdMsg => logMessage(msg) case m: GetLockSettingsReqMsg => logMessage(msg) case m: LockSettingsNotInitializedRespMsg => logMessage(msg) + case m: MeetingInfoAnalyticsMessage => logMessage(msg) case _ => // ignore message } 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 acf224f7aa..dd478beb9c 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 @@ -167,6 +167,16 @@ object MsgBuilder { BbbCommonEnvCoreMsg(envelope, event) } + def buildMeetingInfoAnalyticsMsg(analytics: MeetingInfoAnalytics): BbbCommonEnvCoreMsg = { + val routing = collection.immutable.HashMap("sender" -> "bbb-apps-akka") + val envelope = BbbCoreEnvelope(MeetingInfoAnalyticsMessage.NAME, routing) + val header = BbbCoreBaseHeader(MeetingInfoAnalyticsMessage.NAME) + val body = MeetingInfoAnalyticsMessageBody(analytics) + val event = MeetingInfoAnalyticsMessage(header, body) + + BbbCommonEnvCoreMsg(envelope, event) + } + def buildMeetingDestroyedEvtMsg(meetingId: String): BbbCommonEnvCoreMsg = { val routing = collection.immutable.HashMap("sender" -> "bbb-apps-akka") val envelope = BbbCoreEnvelope(MeetingDestroyedEvtMsg.NAME, routing) diff --git a/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/MeetingInfoAnalyticsMessages.scala b/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/MeetingInfoAnalyticsMessages.scala new file mode 100755 index 0000000000..9f01514dda --- /dev/null +++ b/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/MeetingInfoAnalyticsMessages.scala @@ -0,0 +1,50 @@ +package org.bigbluebutton.common2.msgs + +object MeetingInfoAnalyticsMessage { val NAME = "MeetingInfoAnalyticsMessage" } +case class MeetingInfoAnalyticsMessage( + header: BbbCoreBaseHeader, + body: MeetingInfoAnalyticsMessageBody +) extends BbbCoreMsg +case class MeetingInfoAnalyticsMessageBody(meetingInfo: MeetingInfoAnalytics) + +object MeetingInfoAnalytics { + def apply(meetingName: String, meetingExternalId: String, meetingInternalId: String, hasUserJoined: Boolean, + isRecording: Boolean, numberOfVideos: Int, numberOfUsers: Int, numberOfVoiceUsers: Int, + webcam: Webcam, audio: Audio, screenshare: Screenshare, users: List[String], presentation: Presentation, + breakoutRoom: BreakoutRoom): MeetingInfoAnalytics = + new MeetingInfoAnalytics(meetingName, meetingExternalId, meetingInternalId, hasUserJoined, + isRecording, numberOfVideos, numberOfVoiceUsers, webcam, audio, screenshare, users, presentation, breakoutRoom) +} + +case class MeetingInfoAnalytics( + meetingName: String, + meetingExternalId: String, + meetingInternalId: String, + hasUserJoined: Boolean, + isRecording: Boolean, + numberOfVideos: Int, + numberOfVoiceUsers: Int, + webcam: Webcam, + audio: Audio, + screenshare: Screenshare, + users: List[String], + presentation: Presentation, + breakoutRoom: BreakoutRoom +) + +case class Webcam(totalWebcams: Int, webcamDetails: WebcamDetail) +case class WebcamDetail(broadcastId: String, viewers: List[String]) + +case class Audio(totalVoiceUsers: Int, totalListenOnlyUsers: Int, listeners: List[String]) + +case class Screenshare(name: String) + +case class Presentation(id: String, name: String) +case class BreakoutRoom(parentId: String, rooms: List[String]) +// number of webcam streams and viewers {total: 123, [{broadcast, viewers:[]}]} +// audio streams listeners and two ways +// screenshare +// users +// current presentation id and name +// breakout rooms id and name + From f59886214973bd8c567fcdf3991de010cd4fc269 Mon Sep 17 00:00:00 2001 From: Aron Engineer Date: Fri, 19 Mar 2021 19:21:15 +0000 Subject: [PATCH 02/14] feat: Presenter added to presentationInfo analytics message --- .../core/running/MeetingActor.scala | 27 ++++++++++++------- .../msgs/MeetingInfoAnalyticsMessages.scala | 16 ++++------- 2 files changed, 22 insertions(+), 21 deletions(-) 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 152d2f4976..14f7b84069 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 @@ -31,7 +31,6 @@ import org.bigbluebutton.core.apps.voice._ import akka.actor.Props import akka.actor.OneForOneStrategy import akka.actor.SupervisorStrategy.Resume -import org.bigbluebutton.common2.msgs import scala.concurrent.duration._ import org.bigbluebutton.core.apps.layout.LayoutApp2x @@ -211,8 +210,8 @@ class MeetingActor( ) context.system.scheduler.schedule( - 5 seconds, - 5 second, + 60 seconds, + 60 seconds, self, MeetingInfoAnalyticsMsg ) @@ -510,11 +509,11 @@ class MeetingActor( } def handleMeetingInfoAnalyticsLogging(): Unit = { - println("***********Reaches here************") val meetingName: String = liveMeeting.props.meetingProp.name val externalId: String = liveMeeting.props.meetingProp.extId val internalId: String = liveMeeting.props.meetingProp.intId val hasUserJoined: Boolean = hasAuthedUserJoined(liveMeeting.status) + val isRecording: Boolean = isVoiceRecording(liveMeeting.status) val liveWebcams: Vector[WebcamStream] = findAll(liveMeeting.webcams) @@ -537,7 +536,17 @@ class MeetingActor( val listOfUsers: List[String] = Users2x.findAll(liveMeeting.users2x).map(_.name).toList - val presentation: msgs.Presentation = msgs.Presentation("test", "test") + val presentationId: String = state.presentationPodManager.getAllPresentationPodsInMeeting() + .flatMap(_.getCurrentPresentation.map(_.id)) + .mkString + + val presentationName: String = state.presentationPodManager.getAllPresentationPodsInMeeting() + .flatMap(_.getCurrentPresentation.map(_.name)) + .mkString + + val presenter: Option[String] = Users2x.findPresenter(liveMeeting.users2x).map(_.name) + + val presentationInfo = PresentationInfo(presentationId, presentationName, presenter.getOrElse("")) val breakoutRoom: BreakoutRoom = BreakoutRoom( liveMeeting.props.breakoutProps.parentId, @@ -545,12 +554,10 @@ class MeetingActor( ) val meetingInfoAnalyticsLogMessage: MeetingInfoAnalytics = MeetingInfoAnalytics(meetingName, externalId, internalId, - hasUserJoined, isRecording, numOfLiveWebcams, numOfVoiceUsers, - webcam, audio, screenshare, listOfUsers, presentation, breakoutRoom) + hasUserJoined, isRecording, numOfLiveWebcams, numOfVoiceUsers, webcam, audio, screenshare, listOfUsers, + presentationInfo, breakoutRoom) - val event = MsgBuilder.buildMeetingInfoAnalyticsMsg( - meetingInfoAnalyticsLogMessage - ) + val event = MsgBuilder.buildMeetingInfoAnalyticsMsg(meetingInfoAnalyticsLogMessage) outGW.send(event) } diff --git a/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/MeetingInfoAnalyticsMessages.scala b/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/MeetingInfoAnalyticsMessages.scala index 9f01514dda..b874e365c0 100755 --- a/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/MeetingInfoAnalyticsMessages.scala +++ b/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/MeetingInfoAnalyticsMessages.scala @@ -10,10 +10,10 @@ case class MeetingInfoAnalyticsMessageBody(meetingInfo: MeetingInfoAnalytics) object MeetingInfoAnalytics { def apply(meetingName: String, meetingExternalId: String, meetingInternalId: String, hasUserJoined: Boolean, isRecording: Boolean, numberOfVideos: Int, numberOfUsers: Int, numberOfVoiceUsers: Int, - webcam: Webcam, audio: Audio, screenshare: Screenshare, users: List[String], presentation: Presentation, + webcam: Webcam, audio: Audio, screenshare: Screenshare, users: List[String], presentationInfo: PresentationInfo, breakoutRoom: BreakoutRoom): MeetingInfoAnalytics = - new MeetingInfoAnalytics(meetingName, meetingExternalId, meetingInternalId, hasUserJoined, - isRecording, numberOfVideos, numberOfVoiceUsers, webcam, audio, screenshare, users, presentation, breakoutRoom) + new MeetingInfoAnalytics(meetingName, meetingExternalId, meetingInternalId, hasUserJoined, isRecording, + numberOfVideos, numberOfVoiceUsers, webcam, audio, screenshare, users, presentationInfo, breakoutRoom) } case class MeetingInfoAnalytics( @@ -28,7 +28,7 @@ case class MeetingInfoAnalytics( audio: Audio, screenshare: Screenshare, users: List[String], - presentation: Presentation, + presentationInfo: PresentationInfo, breakoutRoom: BreakoutRoom ) @@ -39,12 +39,6 @@ case class Audio(totalVoiceUsers: Int, totalListenOnlyUsers: Int, listeners: Lis case class Screenshare(name: String) -case class Presentation(id: String, name: String) +case class PresentationInfo(id: String, name: String, presenter: String) case class BreakoutRoom(parentId: String, rooms: List[String]) -// number of webcam streams and viewers {total: 123, [{broadcast, viewers:[]}]} -// audio streams listeners and two ways -// screenshare -// users -// current presentation id and name -// breakout rooms id and name From 6d100d2a21d42aa4be7725f9c0ee1c019d811ca9 Mon Sep 17 00:00:00 2001 From: Aron Engineer Date: Tue, 23 Mar 2021 20:13:40 +0000 Subject: [PATCH 03/14] WIP: New params added to MeetingInfoAnalyticsLogMessage --- .../core/models/VoiceUsers.scala | 1 + .../core/running/MeetingActor.scala | 57 ++++++++++++------- .../bigbluebutton/core2/AnalyticsActor.scala | 1 - .../msgs/MeetingInfoAnalyticsMessages.scala | 55 +++++++++--------- 4 files changed, 67 insertions(+), 47 deletions(-) diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/models/VoiceUsers.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/models/VoiceUsers.scala index 58cfb7dcdc..10ac1b29dd 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/models/VoiceUsers.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/models/VoiceUsers.scala @@ -14,6 +14,7 @@ object VoiceUsers { def findAll(users: VoiceUsers): Vector[VoiceUserState] = users.toVector def findAllNonListenOnlyVoiceUsers(users: VoiceUsers): Vector[VoiceUserState] = users.toVector.filter(u => u.listenOnly == false) + def findAllListenOnlyVoiceUsers(users: VoiceUsers): Vector[VoiceUserState] = users.toVector.filter(u => u.listenOnly == true) def findAllFreeswitchCallers(users: VoiceUsers): Vector[VoiceUserState] = users.toVector.filter(u => u.calledInto == "freeswitch") def findAllKurentoCallers(users: VoiceUsers): Vector[VoiceUserState] = users.toVector.filter(u => u.calledInto == "kms") 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 14f7b84069..9af1dcd734 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 @@ -31,12 +31,13 @@ import org.bigbluebutton.core.apps.voice._ import akka.actor.Props import akka.actor.OneForOneStrategy import akka.actor.SupervisorStrategy.Resume +import org.bigbluebutton.common2.msgs import scala.concurrent.duration._ import org.bigbluebutton.core.apps.layout.LayoutApp2x import org.bigbluebutton.core.apps.meeting.{ SyncGetMeetingInfoRespMsgHdlr, ValidateConnAuthTokenSysMsgHdlr } import org.bigbluebutton.core.apps.users.ChangeLockSettingsInMeetingCmdMsgHdlr -import org.bigbluebutton.core.models.VoiceUsers.findAllNonListenOnlyVoiceUsers +import org.bigbluebutton.core.models.VoiceUsers.findAllListenOnlyVoiceUsers import org.bigbluebutton.core.models.Webcams.findAll import org.bigbluebutton.core2.MeetingStatus2x.{ hasAuthedUserJoined, isVoiceRecording } import org.bigbluebutton.core2.message.senders.{ MsgBuilder, Sender } @@ -516,25 +517,10 @@ class MeetingActor( val isRecording: Boolean = isVoiceRecording(liveMeeting.status) - val liveWebcams: Vector[WebcamStream] = findAll(liveMeeting.webcams) - val numOfLiveWebcams: Int = liveWebcams.length - val mediaStreamLiveWebcamUserId: String = liveWebcams.flatMap(_.stream.userId).mkString - val viewers: List[String] = findAll(liveMeeting.webcams).flatMap(_.stream.viewers).toList - - val webcamDetail: WebcamDetail = WebcamDetail(mediaStreamLiveWebcamUserId, viewers) - val webcam: Webcam = Webcam(numOfLiveWebcams, webcamDetail) - - val voiceUsers: Vector[VoiceUserState] = VoiceUsers.findAll(liveMeeting.voiceUsers) - val numOfVoiceUsers: Int = voiceUsers.length - val numOfListenOnlyUsers: Int = numOfVoiceUsers - findAllNonListenOnlyVoiceUsers(liveMeeting.voiceUsers).length - val listeners: List[String] = voiceUsers.map(_.voiceUserId).toList - - val audio: Audio = Audio(numOfVoiceUsers, numOfListenOnlyUsers, listeners) - val screenshareConfName: String = liveMeeting.props.screenshareProps.screenshareConf val screenshare: Screenshare = Screenshare(screenshareConfName) - val listOfUsers: List[String] = Users2x.findAll(liveMeeting.users2x).map(_.name).toList + val listOfUsers: List[UserState] = Users2x.findAll(liveMeeting.users2x).toList val presentationId: String = state.presentationPodManager.getAllPresentationPodsInMeeting() .flatMap(_.getCurrentPresentation.map(_.id)) @@ -553,15 +539,46 @@ class MeetingActor( liveMeeting.props.breakoutProps.breakoutRooms.toList ) - val meetingInfoAnalyticsLogMessage: MeetingInfoAnalytics = MeetingInfoAnalytics(meetingName, externalId, internalId, - hasUserJoined, isRecording, numOfLiveWebcams, numOfVoiceUsers, webcam, audio, screenshare, listOfUsers, - presentationInfo, breakoutRoom) + val meetingInfoAnalyticsLogMessage: MeetingInfoAnalytics = MeetingInfoAnalytics( + meetingName, + externalId, internalId, + hasUserJoined, isRecording, getMeetingInfoWebcamDetails, + getMeetingInfoAudioDetails, screenshare, + listOfUsers.map(u => Participant(u.intId, u.name, u.role)), presentationInfo, + breakoutRoom + ) val event = MsgBuilder.buildMeetingInfoAnalyticsMsg(meetingInfoAnalyticsLogMessage) outGW.send(event) } + private def getMeetingInfoWebcamDetails(): Webcam = { + val liveWebcams: Vector[org.bigbluebutton.core.models.WebcamStream] = findAll(liveMeeting.webcams) + val numOfLiveWebcams: Int = liveWebcams.length + + val broadcastId: String = liveWebcams.flatMap(_.streamId).mkString + val user: User = User(liveWebcams.flatMap(_.stream.userId).mkString, "") + val startedOn: Long = 0L + val broadcast: Broadcast = Broadcast(broadcastId, user, startedOn) + val viewers: Set[String] = findAll(liveMeeting.webcams).flatMap(_.stream.viewers).toSet + + val webcamStream: WebcamStream = msgs.WebcamStream(broadcast, viewers) + + Webcam(numOfLiveWebcams, webcamStream) + } + + private def getMeetingInfoAudioDetails(): Audio = { + val voiceUsers: Vector[VoiceUserState] = VoiceUsers.findAll(liveMeeting.voiceUsers) + val numOfVoiceUsers: Int = voiceUsers.length + + val listenOnlyUsers: Vector[VoiceUserState] = findAllListenOnlyVoiceUsers(liveMeeting.voiceUsers) + val numOfListenOnlyUsers: Int = listenOnlyUsers.length + val listenOnlyAudioUser = ListenOnlyAudioUser(numOfListenOnlyUsers, listenOnlyUsers.map(vsu => User(vsu.voiceUserId, "")).toList) + + Audio(numOfVoiceUsers, listenOnlyAudioUser, null, null) + } + def handleGetRunningMeetingStateReqMsg(msg: GetRunningMeetingStateReqMsg): Unit = { processGetRunningMeetingStateReqMsg() } 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 a79abad4f5..6e0c0f26ae 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 @@ -3,7 +3,6 @@ package org.bigbluebutton.core2 import akka.actor.{ Actor, ActorLogging, Props } import org.bigbluebutton.common2.msgs._ import org.bigbluebutton.common2.util.JsonUtil -import org.bigbluebutton.common2.msgs.MeetingInfoAnalytics object AnalyticsActor { def props(): Props = Props(classOf[AnalyticsActor]) } diff --git a/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/MeetingInfoAnalyticsMessages.scala b/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/MeetingInfoAnalyticsMessages.scala index b874e365c0..99a2cd3bd5 100755 --- a/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/MeetingInfoAnalyticsMessages.scala +++ b/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/MeetingInfoAnalyticsMessages.scala @@ -8,37 +8,40 @@ case class MeetingInfoAnalyticsMessage( case class MeetingInfoAnalyticsMessageBody(meetingInfo: MeetingInfoAnalytics) object MeetingInfoAnalytics { - def apply(meetingName: String, meetingExternalId: String, meetingInternalId: String, hasUserJoined: Boolean, - isRecording: Boolean, numberOfVideos: Int, numberOfUsers: Int, numberOfVoiceUsers: Int, - webcam: Webcam, audio: Audio, screenshare: Screenshare, users: List[String], presentationInfo: PresentationInfo, - breakoutRoom: BreakoutRoom): MeetingInfoAnalytics = - new MeetingInfoAnalytics(meetingName, meetingExternalId, meetingInternalId, hasUserJoined, isRecording, - numberOfVideos, numberOfVoiceUsers, webcam, audio, screenshare, users, presentationInfo, breakoutRoom) + def apply(name: String, externalId: String, internalId: String, hasUserJoined: Boolean, isRecording: Boolean, webcam: Webcam, + audio: Audio, screenshare: Screenshare, users: List[Participant], presentation: PresentationInfo, + breakoutRooms: BreakoutRoom): MeetingInfoAnalytics = + new MeetingInfoAnalytics(name, externalId, internalId, hasUserJoined, isRecording, webcam, audio, screenshare, users, + presentation, breakoutRooms) } case class MeetingInfoAnalytics( - meetingName: String, - meetingExternalId: String, - meetingInternalId: String, - hasUserJoined: Boolean, - isRecording: Boolean, - numberOfVideos: Int, - numberOfVoiceUsers: Int, - webcam: Webcam, - audio: Audio, - screenshare: Screenshare, - users: List[String], - presentationInfo: PresentationInfo, - breakoutRoom: BreakoutRoom + name: String, + externalId: String, + internalId: String, + hasUserJoined: Boolean, + isRecording: Boolean, + webcams: Webcam, + audio: Audio, + screenshare: Screenshare, + users: List[Participant], + presentation: PresentationInfo, + breakoutRoom: BreakoutRoom ) -case class Webcam(totalWebcams: Int, webcamDetails: WebcamDetail) -case class WebcamDetail(broadcastId: String, viewers: List[String]) +case class Webcam(total: Int, streams: List[WebcamStream]) +case class WebcamStream(broadcast: Broadcast, viewers: Set[String]) +case class User(id: String, name: String) +case class Broadcast(id: String, user: User, startedOn: Long) -case class Audio(totalVoiceUsers: Int, totalListenOnlyUsers: Int, listeners: List[String]) +case class Audio(total: Int, listenOnly: ListenOnlyAudioUser, twoWay: TwoWayAudioUser, phone: PhoneAudioUser) +case class ListenOnlyAudioUser(total: Int, users: List[User]) +case class TwoWayAudioUser(total: Int, users: List[User]) +case class PhoneAudioUser(total: Int, users: List[User]) -case class Screenshare(name: String) - -case class PresentationInfo(id: String, name: String, presenter: String) -case class BreakoutRoom(parentId: String, rooms: List[String]) +case class Screenshare(stream: ScreenshareStream) +case class ScreenshareStream(user: User, viewers: List[User]) +case class Participant(id: String, name: String, role: String) +case class PresentationInfo(id: String, name: String) +case class BreakoutRoom(id: String, names: List[String]) \ No newline at end of file From fe898bc276c4bb9613141516efd243540cff6690 Mon Sep 17 00:00:00 2001 From: Aron Engineer Date: Wed, 24 Mar 2021 20:30:01 +0000 Subject: [PATCH 04/14] WIP: Meeting actor modified to separate individual params --- .../core/running/MeetingActor.scala | 38 ++++++++++--------- 1 file changed, 20 insertions(+), 18 deletions(-) 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 9af1dcd734..dc32ab0e93 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 @@ -32,6 +32,7 @@ import akka.actor.Props import akka.actor.OneForOneStrategy import akka.actor.SupervisorStrategy.Resume import org.bigbluebutton.common2.msgs +import org.bigbluebutton.core.apps.ScreenshareModel.getScreenshareConf import scala.concurrent.duration._ import org.bigbluebutton.core.apps.layout.LayoutApp2x @@ -517,23 +518,12 @@ class MeetingActor( val isRecording: Boolean = isVoiceRecording(liveMeeting.status) - val screenshareConfName: String = liveMeeting.props.screenshareProps.screenshareConf - val screenshare: Screenshare = Screenshare(screenshareConfName) + // val screenshareConfName: String = getScreenshareConf(liveMeeting.screenshareModel) + // val screenshareStream: ScreenshareStream = liveMeeting.screenshareModel + val screenshare: Screenshare = Screenshare(null) val listOfUsers: List[UserState] = Users2x.findAll(liveMeeting.users2x).toList - val presentationId: String = state.presentationPodManager.getAllPresentationPodsInMeeting() - .flatMap(_.getCurrentPresentation.map(_.id)) - .mkString - - val presentationName: String = state.presentationPodManager.getAllPresentationPodsInMeeting() - .flatMap(_.getCurrentPresentation.map(_.name)) - .mkString - - val presenter: Option[String] = Users2x.findPresenter(liveMeeting.users2x).map(_.name) - - val presentationInfo = PresentationInfo(presentationId, presentationName, presenter.getOrElse("")) - val breakoutRoom: BreakoutRoom = BreakoutRoom( liveMeeting.props.breakoutProps.parentId, liveMeeting.props.breakoutProps.breakoutRooms.toList @@ -544,8 +534,8 @@ class MeetingActor( externalId, internalId, hasUserJoined, isRecording, getMeetingInfoWebcamDetails, getMeetingInfoAudioDetails, screenshare, - listOfUsers.map(u => Participant(u.intId, u.name, u.role)), presentationInfo, - breakoutRoom + listOfUsers.map(u => Participant(u.intId, u.name, u.role)), + getMeetingInfoPresentationDetails, breakoutRoom ) val event = MsgBuilder.buildMeetingInfoAnalyticsMsg(meetingInfoAnalyticsLogMessage) @@ -563,9 +553,9 @@ class MeetingActor( val broadcast: Broadcast = Broadcast(broadcastId, user, startedOn) val viewers: Set[String] = findAll(liveMeeting.webcams).flatMap(_.stream.viewers).toSet - val webcamStream: WebcamStream = msgs.WebcamStream(broadcast, viewers) + val webcamStream: msgs.WebcamStream = msgs.WebcamStream(broadcast, viewers) - Webcam(numOfLiveWebcams, webcamStream) + Webcam(numOfLiveWebcams, List(webcamStream)) } private def getMeetingInfoAudioDetails(): Audio = { @@ -579,6 +569,18 @@ class MeetingActor( Audio(numOfVoiceUsers, listenOnlyAudioUser, null, null) } + private def getMeetingInfoPresentationDetails(): PresentationInfo = { + val presentationId: String = state.presentationPodManager.getAllPresentationPodsInMeeting() + .flatMap(_.getCurrentPresentation.map(_.id)) + .mkString + + val presentationName: String = state.presentationPodManager.getAllPresentationPodsInMeeting() + .flatMap(_.getCurrentPresentation.map(_.name)) + .mkString + + PresentationInfo(presentationId, presentationName) + } + def handleGetRunningMeetingStateReqMsg(msg: GetRunningMeetingStateReqMsg): Unit = { processGetRunningMeetingStateReqMsg() } From 7195a67e4e469f2cadd416348023814181b98578 Mon Sep 17 00:00:00 2001 From: Aron Engineer Date: Fri, 26 Mar 2021 20:29:27 +0000 Subject: [PATCH 05/14] fix: MeetingInfoAnalyticsMessage WebcamStream modified --- .../core/running/MeetingActor.scala | 56 +++++++++++-------- .../msgs/MeetingInfoAnalyticsMessages.scala | 4 +- 2 files changed, 36 insertions(+), 24 deletions(-) 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 dc32ab0e93..fbfb73f6f6 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 @@ -19,7 +19,7 @@ import org.bigbluebutton.core.apps.presentation.PresentationApp2x import org.bigbluebutton.core.apps.users.UsersApp2x import org.bigbluebutton.core.apps.whiteboard.WhiteboardApp2x import org.bigbluebutton.core.bus._ -import org.bigbluebutton.core.models.{ VoiceUsers, _ } +import org.bigbluebutton.core.models.{ Users2x, VoiceUsers, _ } import org.bigbluebutton.core2.{ MeetingStatus2x, Permissions } import org.bigbluebutton.core2.message.handlers._ import org.bigbluebutton.core2.message.handlers.meeting._ @@ -32,7 +32,7 @@ import akka.actor.Props import akka.actor.OneForOneStrategy import akka.actor.SupervisorStrategy.Resume import org.bigbluebutton.common2.msgs -import org.bigbluebutton.core.apps.ScreenshareModel.getScreenshareConf +import org.bigbluebutton.core.apps.ScreenshareModel.{ getScreenshareConf, getTimestamp } import scala.concurrent.duration._ import org.bigbluebutton.core.apps.layout.LayoutApp2x @@ -518,20 +518,24 @@ class MeetingActor( val isRecording: Boolean = isVoiceRecording(liveMeeting.status) - // val screenshareConfName: String = getScreenshareConf(liveMeeting.screenshareModel) - // val screenshareStream: ScreenshareStream = liveMeeting.screenshareModel - val screenshare: Screenshare = Screenshare(null) + val screenshare: Screenshare = Screenshare(null) // TODO: Placeholder null as required values not available val listOfUsers: List[UserState] = Users2x.findAll(liveMeeting.users2x).toList + val breakoutRoomNames: List[String] = { + if (state.breakout.isDefined) + state.breakout.get.getRooms.map(_.name).toList + else + List() + } + val breakoutRoom: BreakoutRoom = BreakoutRoom( liveMeeting.props.breakoutProps.parentId, - liveMeeting.props.breakoutProps.breakoutRooms.toList + breakoutRoomNames ) val meetingInfoAnalyticsLogMessage: MeetingInfoAnalytics = MeetingInfoAnalytics( - meetingName, - externalId, internalId, + meetingName, externalId, internalId, hasUserJoined, isRecording, getMeetingInfoWebcamDetails, getMeetingInfoAudioDetails, screenshare, listOfUsers.map(u => Participant(u.intId, u.name, u.role)), @@ -543,19 +547,26 @@ class MeetingActor( outGW.send(event) } + private def resolveUserName(userId: String): String = { + val userName: String = Users2x.findWithIntId(liveMeeting.users2x, userId).map(_.name).getOrElse("") + + userName + } + private def getMeetingInfoWebcamDetails(): Webcam = { val liveWebcams: Vector[org.bigbluebutton.core.models.WebcamStream] = findAll(liveMeeting.webcams) val numOfLiveWebcams: Int = liveWebcams.length - val broadcastId: String = liveWebcams.flatMap(_.streamId).mkString - val user: User = User(liveWebcams.flatMap(_.stream.userId).mkString, "") - val startedOn: Long = 0L - val broadcast: Broadcast = Broadcast(broadcastId, user, startedOn) - val viewers: Set[String] = findAll(liveMeeting.webcams).flatMap(_.stream.viewers).toSet + val broadcasts: List[Broadcast] = liveWebcams.map(webcam => Broadcast( + webcam.stream.id, + User(webcam.stream.userId, resolveUserName(webcam.stream.userId)), 0L + )).toList - val webcamStream: msgs.WebcamStream = msgs.WebcamStream(broadcast, viewers) + val viewers: Set[String] = liveWebcams.flatMap(_.stream.viewers).toSet - Webcam(numOfLiveWebcams, List(webcamStream)) + val webcamStream: msgs.WebcamStream = msgs.WebcamStream(broadcasts, viewers) + + Webcam(numOfLiveWebcams, webcamStream) } private def getMeetingInfoAudioDetails(): Audio = { @@ -564,19 +575,20 @@ class MeetingActor( val listenOnlyUsers: Vector[VoiceUserState] = findAllListenOnlyVoiceUsers(liveMeeting.voiceUsers) val numOfListenOnlyUsers: Int = listenOnlyUsers.length - val listenOnlyAudioUser = ListenOnlyAudioUser(numOfListenOnlyUsers, listenOnlyUsers.map(vsu => User(vsu.voiceUserId, "")).toList) + val listenOnlyAudioUser = ListenOnlyAudioUser( + numOfListenOnlyUsers, + listenOnlyUsers.map(vsu => User(vsu.voiceUserId, resolveUserName(vsu.voiceUserId))).toList + ) Audio(numOfVoiceUsers, listenOnlyAudioUser, null, null) } private def getMeetingInfoPresentationDetails(): PresentationInfo = { - val presentationId: String = state.presentationPodManager.getAllPresentationPodsInMeeting() - .flatMap(_.getCurrentPresentation.map(_.id)) - .mkString + val presentationPods: Vector[PresentationPod] = state.presentationPodManager.getAllPresentationPodsInMeeting() - val presentationName: String = state.presentationPodManager.getAllPresentationPodsInMeeting() - .flatMap(_.getCurrentPresentation.map(_.name)) - .mkString + val presentationId: String = presentationPods.flatMap(_.getCurrentPresentation.map(_.id)).mkString + + val presentationName: String = presentationPods.flatMap(_.getCurrentPresentation.map(_.name)).mkString PresentationInfo(presentationId, presentationName) } diff --git a/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/MeetingInfoAnalyticsMessages.scala b/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/MeetingInfoAnalyticsMessages.scala index 99a2cd3bd5..fd8ce1c78c 100755 --- a/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/MeetingInfoAnalyticsMessages.scala +++ b/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/MeetingInfoAnalyticsMessages.scala @@ -29,8 +29,8 @@ case class MeetingInfoAnalytics( breakoutRoom: BreakoutRoom ) -case class Webcam(total: Int, streams: List[WebcamStream]) -case class WebcamStream(broadcast: Broadcast, viewers: Set[String]) +case class Webcam(total: Int, streams: WebcamStream) +case class WebcamStream(broadcasts: List[Broadcast], viewers: Set[String]) case class User(id: String, name: String) case class Broadcast(id: String, user: User, startedOn: Long) From 21ad951f5f3bf49a218a7d8fdb56817118811f54 Mon Sep 17 00:00:00 2001 From: Aron Engineer Date: Tue, 30 Mar 2021 20:08:41 +0000 Subject: [PATCH 06/14] feat: TwoWay user params added to MeetingAnalyticsInfo message, Message body modified with correct ref naming --- .../core/running/MeetingActor.scala | 30 ++++++++++--------- .../msgs/MeetingInfoAnalyticsMessages.scala | 8 ++--- 2 files changed, 20 insertions(+), 18 deletions(-) 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 fbfb73f6f6..8a05831e5c 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 @@ -32,13 +32,12 @@ import akka.actor.Props import akka.actor.OneForOneStrategy import akka.actor.SupervisorStrategy.Resume import org.bigbluebutton.common2.msgs -import org.bigbluebutton.core.apps.ScreenshareModel.{ getScreenshareConf, getTimestamp } import scala.concurrent.duration._ import org.bigbluebutton.core.apps.layout.LayoutApp2x import org.bigbluebutton.core.apps.meeting.{ SyncGetMeetingInfoRespMsgHdlr, ValidateConnAuthTokenSysMsgHdlr } import org.bigbluebutton.core.apps.users.ChangeLockSettingsInMeetingCmdMsgHdlr -import org.bigbluebutton.core.models.VoiceUsers.findAllListenOnlyVoiceUsers +import org.bigbluebutton.core.models.VoiceUsers.{ findAllFreeswitchCallers, findAllListenOnlyVoiceUsers } import org.bigbluebutton.core.models.Webcams.findAll import org.bigbluebutton.core2.MeetingStatus2x.{ hasAuthedUserJoined, isVoiceRecording } import org.bigbluebutton.core2.message.senders.{ MsgBuilder, Sender } @@ -529,17 +528,11 @@ class MeetingActor( List() } - val breakoutRoom: BreakoutRoom = BreakoutRoom( - liveMeeting.props.breakoutProps.parentId, - breakoutRoomNames - ) + val breakoutRoom: BreakoutRoom = BreakoutRoom(liveMeeting.props.breakoutProps.parentId, breakoutRoomNames) val meetingInfoAnalyticsLogMessage: MeetingInfoAnalytics = MeetingInfoAnalytics( - meetingName, externalId, internalId, - hasUserJoined, isRecording, getMeetingInfoWebcamDetails, - getMeetingInfoAudioDetails, screenshare, - listOfUsers.map(u => Participant(u.intId, u.name, u.role)), - getMeetingInfoPresentationDetails, breakoutRoom + meetingName, externalId, internalId, hasUserJoined, isRecording, getMeetingInfoWebcamDetails, getMeetingInfoAudioDetails, + screenshare, listOfUsers.map(u => Participant(u.intId, u.name, u.role)), getMeetingInfoPresentationDetails, breakoutRoom ) val event = MsgBuilder.buildMeetingInfoAnalyticsMsg(meetingInfoAnalyticsLogMessage) @@ -550,6 +543,8 @@ class MeetingActor( private def resolveUserName(userId: String): String = { val userName: String = Users2x.findWithIntId(liveMeeting.users2x, userId).map(_.name).getOrElse("") + if (userName.isEmpty) log.error(s"Failed to map username for id $userId") + userName } @@ -575,12 +570,19 @@ class MeetingActor( val listenOnlyUsers: Vector[VoiceUserState] = findAllListenOnlyVoiceUsers(liveMeeting.voiceUsers) val numOfListenOnlyUsers: Int = listenOnlyUsers.length - val listenOnlyAudioUser = ListenOnlyAudioUser( + val listenOnlyAudio = ListenOnlyAudio( numOfListenOnlyUsers, - listenOnlyUsers.map(vsu => User(vsu.voiceUserId, resolveUserName(vsu.voiceUserId))).toList + listenOnlyUsers.map(vsu => User(vsu.voiceUserId, resolveUserName(vsu.intId))).toList ) - Audio(numOfVoiceUsers, listenOnlyAudioUser, null, null) + val freeswitchUsers: Vector[VoiceUserState] = findAllFreeswitchCallers(liveMeeting.voiceUsers) + val numOfFreeswitchUsers: Int = freeswitchUsers.length + val twoWayAudio = TwoWayAudio( + numOfFreeswitchUsers, + freeswitchUsers.map(vsu => User(vsu.voiceUserId, resolveUserName(vsu.intId))).toList + ) + + Audio(numOfVoiceUsers, listenOnlyAudio, twoWayAudio, null) } private def getMeetingInfoPresentationDetails(): PresentationInfo = { diff --git a/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/MeetingInfoAnalyticsMessages.scala b/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/MeetingInfoAnalyticsMessages.scala index fd8ce1c78c..c0fb9d9afd 100755 --- a/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/MeetingInfoAnalyticsMessages.scala +++ b/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/MeetingInfoAnalyticsMessages.scala @@ -34,10 +34,10 @@ case class WebcamStream(broadcasts: List[Broadcast], viewers: Set[String]) case class User(id: String, name: String) case class Broadcast(id: String, user: User, startedOn: Long) -case class Audio(total: Int, listenOnly: ListenOnlyAudioUser, twoWay: TwoWayAudioUser, phone: PhoneAudioUser) -case class ListenOnlyAudioUser(total: Int, users: List[User]) -case class TwoWayAudioUser(total: Int, users: List[User]) -case class PhoneAudioUser(total: Int, users: List[User]) +case class Audio(total: Int, listenOnly: ListenOnlyAudio, twoWay: TwoWayAudio, phone: PhoneAudio) +case class ListenOnlyAudio(total: Int, users: List[User]) +case class TwoWayAudio(total: Int, users: List[User]) +case class PhoneAudio(total: Int, users: List[User]) case class Screenshare(stream: ScreenshareStream) case class ScreenshareStream(user: User, viewers: List[User]) From 1a2af980cf8e84d5018ec14d34142ff522c53432 Mon Sep 17 00:00:00 2001 From: Aron Engineer Date: Tue, 30 Mar 2021 20:28:01 +0000 Subject: [PATCH 07/14] fix: literal renamed for readability --- .../scala/org/bigbluebutton/core/running/MeetingActor.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 8a05831e5c..09cdd4ec06 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 @@ -572,14 +572,14 @@ class MeetingActor( val numOfListenOnlyUsers: Int = listenOnlyUsers.length val listenOnlyAudio = ListenOnlyAudio( numOfListenOnlyUsers, - listenOnlyUsers.map(vsu => User(vsu.voiceUserId, resolveUserName(vsu.intId))).toList + listenOnlyUsers.map(voiceUserState => User(voiceUserState.voiceUserId, resolveUserName(voiceUserState.intId))).toList ) val freeswitchUsers: Vector[VoiceUserState] = findAllFreeswitchCallers(liveMeeting.voiceUsers) val numOfFreeswitchUsers: Int = freeswitchUsers.length val twoWayAudio = TwoWayAudio( numOfFreeswitchUsers, - freeswitchUsers.map(vsu => User(vsu.voiceUserId, resolveUserName(vsu.intId))).toList + freeswitchUsers.map(voiceUserState => User(voiceUserState.voiceUserId, resolveUserName(voiceUserState.intId))).toList ) Audio(numOfVoiceUsers, listenOnlyAudio, twoWayAudio, null) From 2d740ec2a9adef619fbf6ec2dcfd15f5a558a2c8 Mon Sep 17 00:00:00 2001 From: Aron Engineer Date: Thu, 1 Apr 2021 17:35:09 +0000 Subject: [PATCH 08/14] feat: CamStreamSubscribeSysMsg added with handler and update webcam functionality, ScreenStreamSubscribeSysMsg added --- .../bigbluebutton/core/models/Webcams.scala | 24 +++++++++++++++++++ .../core/running/MeetingActor.scala | 8 ++++++- .../bigbluebutton/core2/AnalyticsActor.scala | 2 +- .../core2/message/senders/MsgBuilder.scala | 18 ++++++++++---- .../msgs/CamStreamSubscribeSysMsg.scala | 14 +++++++++++ ...s.scala => MeetingInfoAnalyticsMsgs.scala} | 8 +++---- .../msgs/ScreenStreamSubscribeSysMsg.scala | 14 +++++++++++ 7 files changed, 78 insertions(+), 10 deletions(-) create mode 100755 bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/CamStreamSubscribeSysMsg.scala rename bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/{MeetingInfoAnalyticsMessages.scala => MeetingInfoAnalyticsMsgs.scala} (85%) create mode 100755 bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/ScreenStreamSubscribeSysMsg.scala diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/models/Webcams.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/models/Webcams.scala index 58eee795a2..a99b3c647a 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/models/Webcams.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/models/Webcams.scala @@ -30,6 +30,24 @@ object Webcams { removedStream <- webcams.remove(streamId) } yield removedStream } + + def updateWebcamStream(webcams: Webcams, streamId: String, userId: String): Option[WebcamStream] = { + findWithStreamId(webcams, streamId) match { + case Some(value) => { + val mediaStream: MediaStream = MediaStream(value.stream.id, value.stream.url, userId, value.stream.attributes, + value.stream.viewers) + + val webcamStream: WebcamStream = WebcamStream(streamId, mediaStream) + + webcams.update(streamId, webcamStream) + + Some(webcamStream) + } + case None => { + None + } + } + } } class Webcams { @@ -47,6 +65,12 @@ class Webcams { webcam foreach (u => webcams -= streamId) webcam } + + private def update(streamId: String, webcamStream: WebcamStream): WebcamStream = { + val webcam = remove(streamId) + + save(webcamStream) + } } case class WebcamStream(streamId: String, stream: MediaStream) 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 09cdd4ec06..78e7231f24 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 @@ -38,7 +38,7 @@ import org.bigbluebutton.core.apps.layout.LayoutApp2x import org.bigbluebutton.core.apps.meeting.{ SyncGetMeetingInfoRespMsgHdlr, ValidateConnAuthTokenSysMsgHdlr } import org.bigbluebutton.core.apps.users.ChangeLockSettingsInMeetingCmdMsgHdlr import org.bigbluebutton.core.models.VoiceUsers.{ findAllFreeswitchCallers, findAllListenOnlyVoiceUsers } -import org.bigbluebutton.core.models.Webcams.findAll +import org.bigbluebutton.core.models.Webcams.{ findAll, updateWebcamStream } import org.bigbluebutton.core2.MeetingStatus2x.{ hasAuthedUserJoined, isVoiceRecording } import org.bigbluebutton.core2.message.senders.{ MsgBuilder, Sender } @@ -224,6 +224,8 @@ class MeetingActor( checkVoiceConfIsRunningAndRecording() case MeetingInfoAnalyticsMsg => handleMeetingInfoAnalyticsLogging() + case msg: CamStreamSubscribeSysMsg => + handleCamStreamSubscribeSysMsg(msg) //============================= // 2x messages @@ -509,6 +511,10 @@ class MeetingActor( } } + private def handleCamStreamSubscribeSysMsg(msg: CamStreamSubscribeSysMsg): Unit = { + updateWebcamStream(liveMeeting.webcams, msg.body.streamId, msg.body.userId) + } + def handleMeetingInfoAnalyticsLogging(): Unit = { val meetingName: String = liveMeeting.props.meetingProp.name val externalId: String = liveMeeting.props.meetingProp.extId 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 6e0c0f26ae..dcac995807 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 @@ -158,7 +158,7 @@ class AnalyticsActor extends Actor with ActorLogging { case m: ChangeLockSettingsInMeetingCmdMsg => logMessage(msg) case m: GetLockSettingsReqMsg => logMessage(msg) case m: LockSettingsNotInitializedRespMsg => logMessage(msg) - case m: MeetingInfoAnalyticsMessage => logMessage(msg) + case m: MeetingInfoAnalyticsMsg => logMessage(msg) case _ => // ignore message } 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 fc928d2c89..bed1d1f648 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 @@ -171,10 +171,20 @@ object MsgBuilder { def buildMeetingInfoAnalyticsMsg(analytics: MeetingInfoAnalytics): BbbCommonEnvCoreMsg = { val routing = collection.immutable.HashMap("sender" -> "bbb-apps-akka") - val envelope = BbbCoreEnvelope(MeetingInfoAnalyticsMessage.NAME, routing) - val header = BbbCoreBaseHeader(MeetingInfoAnalyticsMessage.NAME) - val body = MeetingInfoAnalyticsMessageBody(analytics) - val event = MeetingInfoAnalyticsMessage(header, body) + val envelope = BbbCoreEnvelope(MeetingInfoAnalyticsMsg.NAME, routing) + val header = BbbCoreBaseHeader(MeetingInfoAnalyticsMsg.NAME) + val body = MeetingInfoAnalyticsMsgBody(analytics) + val event = MeetingInfoAnalyticsMsg(header, body) + + BbbCommonEnvCoreMsg(envelope, event) + } + + def buildCamStreamSubscribeSysMsg(meetingId: String, userId: String, streamId: String, sfuSessionId: String): BbbCommonEnvCoreMsg = { + val routing = collection.immutable.HashMap("sender" -> "bbb-apps-akka") + val envelope = BbbCoreEnvelope(CamStreamSubscribeSysMsg.NAME, routing) + val header = BbbCoreBaseHeader(CamStreamSubscribeSysMsg.NAME) + val body = CamStreamSubscribeSysMsgBody(meetingId, userId, streamId, sfuSessionId) + val event = CamStreamSubscribeSysMsg(header, body) BbbCommonEnvCoreMsg(envelope, event) } diff --git a/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/CamStreamSubscribeSysMsg.scala b/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/CamStreamSubscribeSysMsg.scala new file mode 100755 index 0000000000..fa7d0ba9b0 --- /dev/null +++ b/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/CamStreamSubscribeSysMsg.scala @@ -0,0 +1,14 @@ +package org.bigbluebutton.common2.msgs + +object CamStreamSubscribeSysMsg { val NAME = "CamStreamSubscribeSysMsg" } +case class CamStreamSubscribeSysMsg( + header: BbbCoreBaseHeader, + body: CamStreamSubscribeSysMsgBody +) extends BbbCoreMsg + +case class CamStreamSubscribeSysMsgBody( + meetingId: String, + userId: String, + streamId: String, + sfuSessionId: String +) diff --git a/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/MeetingInfoAnalyticsMessages.scala b/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/MeetingInfoAnalyticsMsgs.scala similarity index 85% rename from bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/MeetingInfoAnalyticsMessages.scala rename to bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/MeetingInfoAnalyticsMsgs.scala index c0fb9d9afd..7e95d397f7 100755 --- a/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/MeetingInfoAnalyticsMessages.scala +++ b/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/MeetingInfoAnalyticsMsgs.scala @@ -1,11 +1,11 @@ package org.bigbluebutton.common2.msgs -object MeetingInfoAnalyticsMessage { val NAME = "MeetingInfoAnalyticsMessage" } -case class MeetingInfoAnalyticsMessage( +object MeetingInfoAnalyticsMsg { val NAME = "MeetingInfoAnalyticsMsg" } +case class MeetingInfoAnalyticsMsg( header: BbbCoreBaseHeader, - body: MeetingInfoAnalyticsMessageBody + body: MeetingInfoAnalyticsMsgBody ) extends BbbCoreMsg -case class MeetingInfoAnalyticsMessageBody(meetingInfo: MeetingInfoAnalytics) +case class MeetingInfoAnalyticsMsgBody(meetingInfo: MeetingInfoAnalytics) object MeetingInfoAnalytics { def apply(name: String, externalId: String, internalId: String, hasUserJoined: Boolean, isRecording: Boolean, webcam: Webcam, diff --git a/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/ScreenStreamSubscribeSysMsg.scala b/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/ScreenStreamSubscribeSysMsg.scala new file mode 100755 index 0000000000..9c8d3b4a59 --- /dev/null +++ b/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/ScreenStreamSubscribeSysMsg.scala @@ -0,0 +1,14 @@ +package org.bigbluebutton.common2.msgs + +object ScreenStreamSubscribeSysMsg { val NAME = "ScreenStreamSubscribeSysMsg" } +case class ScreenStreamSubscribeSysMsg( + header: BbbCoreBaseHeader, + body: ScreenStreamSubscribeSysMsg +) extends BbbCoreMsg + +case class ScreenStreamSubscribeSysMsgBody( + meetingId: String, + userId: String, + streamId: String, + sfuSessionId: String +) \ No newline at end of file From 66b320c26a60437ff8890d5872a16f8fbaadcda7 Mon Sep 17 00:00:00 2001 From: Aron Engineer Date: Thu, 1 Apr 2021 18:12:36 +0000 Subject: [PATCH 09/14] fix: extra lines removed from updateWebcamStream --- .../src/main/scala/org/bigbluebutton/core/models/Webcams.scala | 3 --- 1 file changed, 3 deletions(-) diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/models/Webcams.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/models/Webcams.scala index a99b3c647a..64304716f8 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/models/Webcams.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/core/models/Webcams.scala @@ -36,11 +36,8 @@ object Webcams { case Some(value) => { val mediaStream: MediaStream = MediaStream(value.stream.id, value.stream.url, userId, value.stream.attributes, value.stream.viewers) - val webcamStream: WebcamStream = WebcamStream(streamId, mediaStream) - webcams.update(streamId, webcamStream) - Some(webcamStream) } case None => { From c7563765c6131330ae2e343fb69e3d78a203c0de Mon Sep 17 00:00:00 2001 From: Aron Engineer Date: Mon, 5 Apr 2021 18:43:39 +0000 Subject: [PATCH 10/14] fix: Analytics logging value Meeting Recorded corrected, common messages change --- .../core/running/MeetingActor.scala | 29 +++++-------------- .../msgs/MeetingInfoAnalyticsMsgs.scala | 28 +++++++++--------- 2 files changed, 22 insertions(+), 35 deletions(-) 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 78e7231f24..90f86cd356 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 @@ -211,8 +211,8 @@ class MeetingActor( ) context.system.scheduler.schedule( - 60 seconds, - 60 seconds, + 1 minute, + 1 minute, self, MeetingInfoAnalyticsMsg ) @@ -226,6 +226,8 @@ class MeetingActor( handleMeetingInfoAnalyticsLogging() case msg: CamStreamSubscribeSysMsg => handleCamStreamSubscribeSysMsg(msg) + case msg: ScreenStreamSubscribeSysMsg => + handleScreenStreamSubscribeSysMsg(msg) //============================= // 2x messages @@ -515,58 +517,47 @@ class MeetingActor( updateWebcamStream(liveMeeting.webcams, msg.body.streamId, msg.body.userId) } + private def handleScreenStreamSubscribeSysMsg(msg: ScreenStreamSubscribeSysMsg): Unit = ??? + def handleMeetingInfoAnalyticsLogging(): Unit = { val meetingName: String = liveMeeting.props.meetingProp.name val externalId: String = liveMeeting.props.meetingProp.extId val internalId: String = liveMeeting.props.meetingProp.intId val hasUserJoined: Boolean = hasAuthedUserJoined(liveMeeting.status) - - val isRecording: Boolean = isVoiceRecording(liveMeeting.status) - + val isMeetingRecorded = MeetingStatus2x.isRecording(liveMeeting.status) val screenshare: Screenshare = Screenshare(null) // TODO: Placeholder null as required values not available val listOfUsers: List[UserState] = Users2x.findAll(liveMeeting.users2x).toList - val breakoutRoomNames: List[String] = { if (state.breakout.isDefined) state.breakout.get.getRooms.map(_.name).toList else List() } - val breakoutRoom: BreakoutRoom = BreakoutRoom(liveMeeting.props.breakoutProps.parentId, breakoutRoomNames) - val meetingInfoAnalyticsLogMessage: MeetingInfoAnalytics = MeetingInfoAnalytics( - meetingName, externalId, internalId, hasUserJoined, isRecording, getMeetingInfoWebcamDetails, getMeetingInfoAudioDetails, + meetingName, externalId, internalId, hasUserJoined, isMeetingRecorded, getMeetingInfoWebcamDetails, getMeetingInfoAudioDetails, screenshare, listOfUsers.map(u => Participant(u.intId, u.name, u.role)), getMeetingInfoPresentationDetails, breakoutRoom ) - val event = MsgBuilder.buildMeetingInfoAnalyticsMsg(meetingInfoAnalyticsLogMessage) - outGW.send(event) } private def resolveUserName(userId: String): String = { val userName: String = Users2x.findWithIntId(liveMeeting.users2x, userId).map(_.name).getOrElse("") - if (userName.isEmpty) log.error(s"Failed to map username for id $userId") - userName } private def getMeetingInfoWebcamDetails(): Webcam = { val liveWebcams: Vector[org.bigbluebutton.core.models.WebcamStream] = findAll(liveMeeting.webcams) val numOfLiveWebcams: Int = liveWebcams.length - val broadcasts: List[Broadcast] = liveWebcams.map(webcam => Broadcast( webcam.stream.id, User(webcam.stream.userId, resolveUserName(webcam.stream.userId)), 0L )).toList - val viewers: Set[String] = liveWebcams.flatMap(_.stream.viewers).toSet - val webcamStream: msgs.WebcamStream = msgs.WebcamStream(broadcasts, viewers) - Webcam(numOfLiveWebcams, webcamStream) } @@ -593,11 +584,8 @@ class MeetingActor( private def getMeetingInfoPresentationDetails(): PresentationInfo = { val presentationPods: Vector[PresentationPod] = state.presentationPodManager.getAllPresentationPodsInMeeting() - val presentationId: String = presentationPods.flatMap(_.getCurrentPresentation.map(_.id)).mkString - val presentationName: String = presentationPods.flatMap(_.getCurrentPresentation.map(_.name)).mkString - PresentationInfo(presentationId, presentationName) } @@ -651,7 +639,6 @@ class MeetingActor( } def handleDeskShareGetDeskShareInfoRequest(msg: DeskShareGetDeskShareInfoRequest): Unit = { - log.info("handleDeskShareGetDeskShareInfoRequest: " + msg.conferenceName + "isBroadcasting=" + ScreenshareModel.isBroadcastingRTMP(liveMeeting.screenshareModel) + " URL:" + ScreenshareModel.getRTMPBroadcastingUrl(liveMeeting.screenshareModel)) diff --git a/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/MeetingInfoAnalyticsMsgs.scala b/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/MeetingInfoAnalyticsMsgs.scala index 7e95d397f7..ddc1dd80d4 100755 --- a/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/MeetingInfoAnalyticsMsgs.scala +++ b/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/MeetingInfoAnalyticsMsgs.scala @@ -8,25 +8,25 @@ case class MeetingInfoAnalyticsMsg( case class MeetingInfoAnalyticsMsgBody(meetingInfo: MeetingInfoAnalytics) object MeetingInfoAnalytics { - def apply(name: String, externalId: String, internalId: String, hasUserJoined: Boolean, isRecording: Boolean, webcam: Webcam, - audio: Audio, screenshare: Screenshare, users: List[Participant], presentation: PresentationInfo, + def apply(name: String, externalId: String, internalId: String, hasUserJoined: Boolean, isMeetingRecorded: Boolean, + webcam: Webcam, audio: Audio, screenshare: Screenshare, users: List[Participant], presentation: PresentationInfo, breakoutRooms: BreakoutRoom): MeetingInfoAnalytics = - new MeetingInfoAnalytics(name, externalId, internalId, hasUserJoined, isRecording, webcam, audio, screenshare, users, + new MeetingInfoAnalytics(name, externalId, internalId, hasUserJoined, isMeetingRecorded, webcam, audio, screenshare, users, presentation, breakoutRooms) } case class MeetingInfoAnalytics( - name: String, - externalId: String, - internalId: String, - hasUserJoined: Boolean, - isRecording: Boolean, - webcams: Webcam, - audio: Audio, - screenshare: Screenshare, - users: List[Participant], - presentation: PresentationInfo, - breakoutRoom: BreakoutRoom + name: String, + externalId: String, + internalId: String, + hasUserJoined: Boolean, + isMeetingRecorded: Boolean, + webcams: Webcam, + audio: Audio, + screenshare: Screenshare, + users: List[Participant], + presentation: PresentationInfo, + breakoutRoom: BreakoutRoom ) case class Webcam(total: Int, streams: WebcamStream) From 35909a4cba9e118fa4c0ec81aaaa9e14626ab2e9 Mon Sep 17 00:00:00 2001 From: Aron Engineer Date: Mon, 5 Apr 2021 20:55:55 +0000 Subject: [PATCH 11/14] fix: Null value replaced with other relevant placeholder values for analytics logging. --- .../org/bigbluebutton/core/running/MeetingActor.scala | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) 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 90f86cd356..cf41e0f5b3 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 @@ -525,7 +525,10 @@ class MeetingActor( val internalId: String = liveMeeting.props.meetingProp.intId val hasUserJoined: Boolean = hasAuthedUserJoined(liveMeeting.status) val isMeetingRecorded = MeetingStatus2x.isRecording(liveMeeting.status) - val screenshare: Screenshare = Screenshare(null) // TODO: Placeholder null as required values not available + + // TODO: Placeholder values as required values not available + val screenshareStream: ScreenshareStream = ScreenshareStream(new User("", ""), List()) + val screenshare: Screenshare = Screenshare(screenshareStream) val listOfUsers: List[UserState] = Users2x.findAll(liveMeeting.users2x).toList val breakoutRoomNames: List[String] = { @@ -579,7 +582,10 @@ class MeetingActor( freeswitchUsers.map(voiceUserState => User(voiceUserState.voiceUserId, resolveUserName(voiceUserState.intId))).toList ) - Audio(numOfVoiceUsers, listenOnlyAudio, twoWayAudio, null) + // TODO: Placeholder values + val phoneAudio = PhoneAudio(0, List()) + + Audio(numOfVoiceUsers, listenOnlyAudio, twoWayAudio, phoneAudio) } private def getMeetingInfoPresentationDetails(): PresentationInfo = { From e5903ccfddbed93421f6aa41457dec89d1549242 Mon Sep 17 00:00:00 2001 From: Aron Engineer Date: Thu, 8 Apr 2021 19:19:22 +0000 Subject: [PATCH 12/14] feat:Meeting analytics web api endpoint added to expose meeting info --- .../scala/org/bigbluebutton/ApiService.scala | 70 +++++++++++++++++-- .../main/scala/org/bigbluebutton/Boot.scala | 14 ++-- .../core/running/MeetingActor.scala | 27 ++++--- .../core2/message/senders/MsgBuilder.scala | 9 +++ .../service/MeetingInfoService.scala | 62 ++++++++++++++++ .../msgs/MeetingInfoAnalyticsServiceMsg.scala | 7 ++ 6 files changed, 172 insertions(+), 17 deletions(-) create mode 100755 akka-bbb-apps/src/main/scala/org/bigbluebutton/service/MeetingInfoService.scala create mode 100755 bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/MeetingInfoAnalyticsServiceMsg.scala diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/ApiService.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/ApiService.scala index 93f4cd5d40..b71d2f4404 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/ApiService.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/ApiService.scala @@ -3,7 +3,8 @@ package org.bigbluebutton import akka.http.scaladsl.model._ import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport import akka.http.scaladsl.server.Directives._ -import org.bigbluebutton.service.{ HealthzService, PubSubReceiveStatus, PubSubSendStatus, RecordingDBSendStatus } +import org.bigbluebutton.common2.msgs._ +import org.bigbluebutton.service.{ HealthzService, MeetingInfoService, PubSubReceiveStatus, PubSubSendStatus, RecordingDBSendStatus } import spray.json.DefaultJsonProtocol case class HealthResponse( @@ -13,14 +14,56 @@ case class HealthResponse( recordingDbStatus: RecordingDBSendStatus ) -trait JsonSupportProtocol extends SprayJsonSupport with DefaultJsonProtocol { +case class MeetingInfoResponse( + meetingInfoResponse: Option[MeetingInfoAnalytics] +) + +case class MeetingInfoAnalytics( + name: String, + externalId: String, + internalId: String, + hasUserJoined: Boolean, + isMeetingRecorded: Boolean, + webcams: Webcam, + audio: Audio, + screenshare: Screenshare, + users: List[Participant], + presentation: PresentationInfo, + breakoutRoom: BreakoutRoom +) + +trait JsonSupportProtocolHealthResponse extends SprayJsonSupport with DefaultJsonProtocol { implicit val pubSubSendStatusJsonFormat = jsonFormat2(PubSubSendStatus) implicit val pubSubReceiveStatusJsonFormat = jsonFormat2(PubSubReceiveStatus) implicit val recordingDbStatusJsonFormat = jsonFormat2(RecordingDBSendStatus) implicit val healthServiceJsonFormat = jsonFormat4(HealthResponse) } -class ApiService(healthz: HealthzService) extends JsonSupportProtocol { +trait JsonSupportProtocolMeetingInfoResponse extends SprayJsonSupport with DefaultJsonProtocol { + implicit val meetingInfoUserJsonFormat = jsonFormat2(User) + implicit val meetingInfoBroadcastJsonFormat = jsonFormat3(Broadcast) + implicit val meetingInfoWebcamStreamJsonFormat = jsonFormat2(WebcamStream) + implicit val meetingInfoWebcamJsonFormat = jsonFormat2(Webcam) + + implicit val meetingInfoListenOnlyAudioJsonFormat = jsonFormat2(ListenOnlyAudio) + implicit val meetingInfoTwoWayAudioJsonFormat = jsonFormat2(TwoWayAudio) + implicit val meetingInfoPhoneAudioJsonFormat = jsonFormat2(PhoneAudio) + implicit val meetingInfoAudioJsonFormat = jsonFormat4(Audio) + + implicit val meetingInfoScreenshareStreamJsonFormat = jsonFormat2(ScreenshareStream) + implicit val meetingInfoScreenshareJsonFormat = jsonFormat1(Screenshare) + + implicit val meetingInfoPresentationInfoJsonFormat = jsonFormat2(PresentationInfo) + implicit val meetingInfoBreakoutRoomJsonFormat = jsonFormat2(BreakoutRoom) + + implicit val meetingInfoParticipantJsonFormat = jsonFormat3(Participant) + implicit val meetingInfoAnalyticsJsonFormat = jsonFormat11(MeetingInfoAnalytics) + implicit val meetingInfoResponseJsonFormat = jsonFormat1(MeetingInfoResponse) +} + +class ApiService(healthz: HealthzService, meetingInfoz: MeetingInfoService) + extends JsonSupportProtocolHealthResponse + with JsonSupportProtocolMeetingInfoResponse { def routes = path("healthz") { @@ -51,5 +94,24 @@ class ApiService(healthz: HealthzService) extends JsonSupportProtocol { } } } - } + } ~ + path("analytics") { + get { + val future = meetingInfoz.getAnalytics() + onSuccess(future) { + case response => + if (response.optionMeetingInfoAnalytics.isDefined) { + complete( + StatusCodes.OK, + MeetingInfoResponse(response.optionMeetingInfoAnalytics) + ) + } else { + complete( + StatusCodes.NoContent, + MeetingInfoResponse(None) + ) + } + } + } + } } diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/Boot.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/Boot.scala index 47310b3e2a..b8b1db0d6d 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/Boot.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/Boot.scala @@ -13,7 +13,7 @@ import org.bigbluebutton.core2.FromAkkaAppsMsgSenderActor import org.bigbluebutton.endpoint.redis.AppsRedisSubscriberActor import org.bigbluebutton.endpoint.redis.RedisRecorderActor import org.bigbluebutton.common2.bus.IncomingJsonMessageBus -import org.bigbluebutton.service.HealthzService +import org.bigbluebutton.service.{ HealthzService, MeetingInfoActor, MeetingInfoService } object Boot extends App with SystemConfiguration { @@ -40,10 +40,18 @@ object Boot extends App with SystemConfiguration { ) val msgSender = new MessageSender(redisPublisher) + val bbbMsgBus = new BbbMsgRouterEventBus val healthzService = HealthzService(system) - val apiService = new ApiService(healthzService) + val meetingInfoActorRef = system.actorOf(MeetingInfoActor.props()) + + outBus2.subscribe(meetingInfoActorRef, outBbbMsgMsgChannel) + bbbMsgBus.subscribe(meetingInfoActorRef, analyticsChannel) + + val meetingInfoService = MeetingInfoService(system, meetingInfoActorRef) + + val apiService = new ApiService(healthzService, meetingInfoService) val redisRecorderActor = system.actorOf( RedisRecorderActor.props(system, redisConfig, healthzService), @@ -53,8 +61,6 @@ object Boot extends App with SystemConfiguration { recordingEventBus.subscribe(redisRecorderActor, outMessageChannel) val incomingJsonMessageBus = new IncomingJsonMessageBus - val bbbMsgBus = new BbbMsgRouterEventBus - val fromAkkaAppsMsgSenderActorRef = system.actorOf(FromAkkaAppsMsgSenderActor.props(msgSender)) val analyticsActorRef = system.actorOf(AnalyticsActor.props()) 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 cf41e0f5b3..5e98ab694a 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 @@ -210,12 +210,13 @@ class MeetingActor( CheckVoiceRecordingInternalMsg ) - context.system.scheduler.schedule( - 1 minute, - 1 minute, - self, - MeetingInfoAnalyticsMsg - ) +// TODO: Aggregation of all meetings required, to expose via api and log it +// context.system.scheduler.schedule( +// 1 minute, +// 1 minute, +// self, +// MeetingInfoAnalyticsMsg +// ) def receive = { case SyncVoiceUserStatusInternalMsg => @@ -520,6 +521,16 @@ class MeetingActor( private def handleScreenStreamSubscribeSysMsg(msg: ScreenStreamSubscribeSysMsg): Unit = ??? def handleMeetingInfoAnalyticsLogging(): Unit = { + val meetingInfoAnalyticsLogMsg: MeetingInfoAnalytics = prepareMeetingInfo() + + val event = MsgBuilder.buildMeetingInfoAnalyticsMsg(meetingInfoAnalyticsLogMsg) + outGW.send(event) + + val event2 = MsgBuilder.buildMeetingInfoAnalyticsServiceMsg(meetingInfoAnalyticsLogMsg) + outGW.send(event2) + } + + private def prepareMeetingInfo(): MeetingInfoAnalytics = { val meetingName: String = liveMeeting.props.meetingProp.name val externalId: String = liveMeeting.props.meetingProp.extId val internalId: String = liveMeeting.props.meetingProp.intId @@ -538,12 +549,10 @@ class MeetingActor( List() } val breakoutRoom: BreakoutRoom = BreakoutRoom(liveMeeting.props.breakoutProps.parentId, breakoutRoomNames) - val meetingInfoAnalyticsLogMessage: MeetingInfoAnalytics = MeetingInfoAnalytics( + MeetingInfoAnalytics( meetingName, externalId, internalId, hasUserJoined, isMeetingRecorded, getMeetingInfoWebcamDetails, getMeetingInfoAudioDetails, screenshare, listOfUsers.map(u => Participant(u.intId, u.name, u.role)), getMeetingInfoPresentationDetails, breakoutRoom ) - val event = MsgBuilder.buildMeetingInfoAnalyticsMsg(meetingInfoAnalyticsLogMessage) - outGW.send(event) } private def resolveUserName(userId: String): String = { 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 bed1d1f648..4e6334c4b7 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 @@ -179,6 +179,15 @@ object MsgBuilder { BbbCommonEnvCoreMsg(envelope, event) } + def buildMeetingInfoAnalyticsServiceMsg(analytics: MeetingInfoAnalytics): BbbCommonEnvCoreMsg = { + val routing = collection.immutable.HashMap("sender" -> "bbb-apps-akka") + val envelope = BbbCoreEnvelope(MeetingInfoAnalyticsServiceMsg.NAME, routing) + val header = BbbCoreBaseHeader(MeetingInfoAnalyticsServiceMsg.NAME) + val body = MeetingInfoAnalyticsMsgBody(analytics) + val event = MeetingInfoAnalyticsServiceMsg(header, body) + BbbCommonEnvCoreMsg(envelope, event) + } + def buildCamStreamSubscribeSysMsg(meetingId: String, userId: String, streamId: String, sfuSessionId: String): BbbCommonEnvCoreMsg = { val routing = collection.immutable.HashMap("sender" -> "bbb-apps-akka") val envelope = BbbCoreEnvelope(CamStreamSubscribeSysMsg.NAME, routing) diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/service/MeetingInfoService.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/service/MeetingInfoService.scala new file mode 100755 index 0000000000..d0dad7be3a --- /dev/null +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/service/MeetingInfoService.scala @@ -0,0 +1,62 @@ +package org.bigbluebutton.service + +import akka.actor.{ Actor, ActorLogging, ActorRef, ActorSystem, Props } +import akka.pattern.ask +import akka.pattern.AskTimeoutException +import akka.util.Timeout +import org.bigbluebutton.MeetingInfoAnalytics +import org.bigbluebutton.common2.msgs.{ BbbCommonEnvCoreMsg, MeetingInfoAnalyticsServiceMsg } + +import scala.concurrent.duration.DurationInt +import scala.concurrent.{ ExecutionContextExecutor, Future } + +sealed trait MeetingInfoMessage + +case object GetMeetingInfoMessage extends MeetingInfoMessage +case class MeetingInfoResponseMsg(optionMeetingInfoAnalytics: Option[MeetingInfoAnalytics]) extends MeetingInfoMessage + +object MeetingInfoService { + def apply(system: ActorSystem, meetingInfoActor: ActorRef) = new MeetingInfoService(system, meetingInfoActor) +} + +class MeetingInfoService(system: ActorSystem, meetingInfoActor: ActorRef) { + implicit def executionContext: ExecutionContextExecutor = system.dispatcher + implicit val timeout: Timeout = 2 seconds + + def getAnalytics(): Future[MeetingInfoResponseMsg] = { + val future = meetingInfoActor.ask(GetMeetingInfoMessage).mapTo[MeetingInfoResponseMsg] + + future.recover { + case e: AskTimeoutException => { + MeetingInfoResponseMsg(None) + } + } + } +} + +object MeetingInfoActor { + def props(): Props = Props(classOf[MeetingInfoActor]) +} + +class MeetingInfoActor extends Actor with ActorLogging { + var optionMeetingInfo: Option[MeetingInfoAnalytics] = None + + override def receive: Receive = { + case msg: BbbCommonEnvCoreMsg => handle(msg) + case GetMeetingInfoMessage => + sender ! MeetingInfoResponseMsg(optionMeetingInfo) + case _ => // ignore other messages + } + + def handle(msg: BbbCommonEnvCoreMsg): Unit = { + msg.core match { + case m: MeetingInfoAnalyticsServiceMsg => { + optionMeetingInfo = Option.apply(MeetingInfoAnalytics(m.body.meetingInfo.name, m.body.meetingInfo.externalId, + m.body.meetingInfo.internalId, m.body.meetingInfo.hasUserJoined, m.body.meetingInfo.isMeetingRecorded, m.body.meetingInfo.webcams, + m.body.meetingInfo.audio, m.body.meetingInfo.screenshare, m.body.meetingInfo.users, m.body.meetingInfo.presentation, + m.body.meetingInfo.breakoutRoom)) + } + case _ => // ignore + } + } +} \ No newline at end of file diff --git a/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/MeetingInfoAnalyticsServiceMsg.scala b/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/MeetingInfoAnalyticsServiceMsg.scala new file mode 100755 index 0000000000..436031198d --- /dev/null +++ b/bbb-common-message/src/main/scala/org/bigbluebutton/common2/msgs/MeetingInfoAnalyticsServiceMsg.scala @@ -0,0 +1,7 @@ +package org.bigbluebutton.common2.msgs + +object MeetingInfoAnalyticsServiceMsg { val NAME = "MeetingInfoAnalyticsServiceMsg" } +case class MeetingInfoAnalyticsServiceMsg( + header: BbbCoreBaseHeader, + body: MeetingInfoAnalyticsMsgBody +) extends BbbCoreMsg From 182340112ac1b55d1c38cbb1e695b7cd74fe377c Mon Sep 17 00:00:00 2001 From: Aron Engineer Date: Fri, 9 Apr 2021 18:47:01 +0000 Subject: [PATCH 13/14] feat: Meeting id query param added, support for get and getall added --- .../scala/org/bigbluebutton/ApiService.scala | 43 ++++++++++------ .../service/MeetingInfoService.scala | 49 +++++++++++++++---- 2 files changed, 66 insertions(+), 26 deletions(-) diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/ApiService.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/ApiService.scala index b71d2f4404..776cf7cc1c 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/ApiService.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/ApiService.scala @@ -5,7 +5,9 @@ import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport import akka.http.scaladsl.server.Directives._ import org.bigbluebutton.common2.msgs._ import org.bigbluebutton.service.{ HealthzService, MeetingInfoService, PubSubReceiveStatus, PubSubSendStatus, RecordingDBSendStatus } -import spray.json.DefaultJsonProtocol +import spray.json._ +import scala.concurrent._ +import ExecutionContext.Implicits.global case class HealthResponse( isHealthy: Boolean, @@ -96,22 +98,31 @@ class ApiService(healthz: HealthzService, meetingInfoz: MeetingInfoService) } } ~ path("analytics") { - get { - val future = meetingInfoz.getAnalytics() - onSuccess(future) { - case response => - if (response.optionMeetingInfoAnalytics.isDefined) { - complete( - StatusCodes.OK, - MeetingInfoResponse(response.optionMeetingInfoAnalytics) - ) - } else { - complete( - StatusCodes.NoContent, - MeetingInfoResponse(None) - ) + parameter('meetingId.as[String]) { meetingId => + get { + val meetingAnalyticsFuture = meetingInfoz.getAnalytics(meetingId) + val entityFuture = meetingAnalyticsFuture.map { resp => + resp.optionMeetingInfoAnalytics match { + case Some(_) => + HttpEntity(ContentTypes.`application/json`, resp.optionMeetingInfoAnalytics.get.toJson.prettyPrint) + case None => + HttpEntity(ContentTypes.`application/json`, {}.toJson.prettyPrint) } + } + complete(entityFuture) + } + } ~ + get { + val future = meetingInfoz.getAnalytics() + val entityFuture = future.map { res => + res.optionMeetingsInfoAnalytics match { + case Some(_) => + HttpEntity(ContentTypes.`application/json`, res.optionMeetingsInfoAnalytics.get.toJson.prettyPrint) + case None => + HttpEntity(ContentTypes.`application/json`, {}.toJson.prettyPrint) + } + } + complete(entityFuture) } - } } } diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/service/MeetingInfoService.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/service/MeetingInfoService.scala index d0dad7be3a..507a2fb409 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/service/MeetingInfoService.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/service/MeetingInfoService.scala @@ -5,15 +5,18 @@ import akka.pattern.ask import akka.pattern.AskTimeoutException import akka.util.Timeout import org.bigbluebutton.MeetingInfoAnalytics -import org.bigbluebutton.common2.msgs.{ BbbCommonEnvCoreMsg, MeetingInfoAnalyticsServiceMsg } +import org.bigbluebutton.common2.msgs.{ BbbCommonEnvCoreMsg, MeetingEndingEvtMsg, MeetingInfoAnalyticsServiceMsg } +import scala.collection.mutable import scala.concurrent.duration.DurationInt import scala.concurrent.{ ExecutionContextExecutor, Future } sealed trait MeetingInfoMessage -case object GetMeetingInfoMessage extends MeetingInfoMessage +case class GetMeetingInfoMessage(meetingId: String) extends MeetingInfoMessage +case object GetMeetingsInfoMessage extends MeetingInfoMessage case class MeetingInfoResponseMsg(optionMeetingInfoAnalytics: Option[MeetingInfoAnalytics]) extends MeetingInfoMessage +case class MeetingInfoListResponseMsg(optionMeetingsInfoAnalytics: Option[List[MeetingInfoAnalytics]]) extends MeetingInfoMessage object MeetingInfoService { def apply(system: ActorSystem, meetingInfoActor: ActorRef) = new MeetingInfoService(system, meetingInfoActor) @@ -23,8 +26,18 @@ class MeetingInfoService(system: ActorSystem, meetingInfoActor: ActorRef) { implicit def executionContext: ExecutionContextExecutor = system.dispatcher implicit val timeout: Timeout = 2 seconds - def getAnalytics(): Future[MeetingInfoResponseMsg] = { - val future = meetingInfoActor.ask(GetMeetingInfoMessage).mapTo[MeetingInfoResponseMsg] + def getAnalytics(): Future[MeetingInfoListResponseMsg] = { + val future = meetingInfoActor.ask(GetMeetingsInfoMessage).mapTo[MeetingInfoListResponseMsg] + + future.recover { + case e: AskTimeoutException => { + MeetingInfoListResponseMsg(None) + } + } + } + + def getAnalytics(meetingId: String): Future[MeetingInfoResponseMsg] = { + val future = meetingInfoActor.ask(GetMeetingInfoMessage(meetingId)).mapTo[MeetingInfoResponseMsg] future.recover { case e: AskTimeoutException => { @@ -40,23 +53,39 @@ object MeetingInfoActor { class MeetingInfoActor extends Actor with ActorLogging { var optionMeetingInfo: Option[MeetingInfoAnalytics] = None + var meetingInfoMap: mutable.HashMap[String, MeetingInfoAnalytics] = mutable.HashMap.empty[String, MeetingInfoAnalytics] override def receive: Receive = { case msg: BbbCommonEnvCoreMsg => handle(msg) - case GetMeetingInfoMessage => - sender ! MeetingInfoResponseMsg(optionMeetingInfo) + case GetMeetingsInfoMessage => + sender ! MeetingInfoListResponseMsg(Option(meetingInfoMap.values.toList)) + case GetMeetingInfoMessage(meetingId) => + meetingInfoMap.get(meetingId) match { + case Some(meetingInfoAnalytics) => + sender ! MeetingInfoResponseMsg(Option(meetingInfoAnalytics)) + case None => sender ! MeetingInfoResponseMsg(None) + } case _ => // ignore other messages } def handle(msg: BbbCommonEnvCoreMsg): Unit = { msg.core match { - case m: MeetingInfoAnalyticsServiceMsg => { + case m: MeetingInfoAnalyticsServiceMsg => + val meetingInternalId = m.body.meetingInfo.internalId + optionMeetingInfo = Option.apply(MeetingInfoAnalytics(m.body.meetingInfo.name, m.body.meetingInfo.externalId, - m.body.meetingInfo.internalId, m.body.meetingInfo.hasUserJoined, m.body.meetingInfo.isMeetingRecorded, m.body.meetingInfo.webcams, + meetingInternalId, m.body.meetingInfo.hasUserJoined, m.body.meetingInfo.isMeetingRecorded, m.body.meetingInfo.webcams, m.body.meetingInfo.audio, m.body.meetingInfo.screenshare, m.body.meetingInfo.users, m.body.meetingInfo.presentation, m.body.meetingInfo.breakoutRoom)) - } - case _ => // ignore + + meetingInfoMap.get(meetingInternalId) match { + case Some(_) => { + meetingInfoMap(meetingInternalId) = optionMeetingInfo.get + } + case None => meetingInfoMap += (meetingInternalId -> optionMeetingInfo.get) + } + case m: MeetingEndingEvtMsg => meetingInfoMap -= m.body.meetingId + case _ => // ignore } } } \ No newline at end of file From 90fd6f4d2fe68b6360dd0be734096aa50b483a24 Mon Sep 17 00:00:00 2001 From: paultrudel Date: Fri, 11 Jun 2021 09:31:51 -0400 Subject: [PATCH 14/14] analytics endpoint now successfully returns meeting info --- .../scala/org/bigbluebutton/ApiService.scala | 4 +-- .../core/running/MeetingActor.scala | 30 ++++++++++++------- .../service/MeetingInfoService.scala | 6 +++- 3 files changed, 27 insertions(+), 13 deletions(-) diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/ApiService.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/ApiService.scala index 776cf7cc1c..3ee84a44ee 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/ApiService.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/ApiService.scala @@ -106,7 +106,7 @@ class ApiService(healthz: HealthzService, meetingInfoz: MeetingInfoService) case Some(_) => HttpEntity(ContentTypes.`application/json`, resp.optionMeetingInfoAnalytics.get.toJson.prettyPrint) case None => - HttpEntity(ContentTypes.`application/json`, {}.toJson.prettyPrint) + HttpEntity(ContentTypes.`application/json`, s"""{ "message": "No active meeting with ID $meetingId"}""".parseJson.prettyPrint) } } complete(entityFuture) @@ -119,7 +119,7 @@ class ApiService(healthz: HealthzService, meetingInfoz: MeetingInfoService) case Some(_) => HttpEntity(ContentTypes.`application/json`, res.optionMeetingsInfoAnalytics.get.toJson.prettyPrint) case None => - HttpEntity(ContentTypes.`application/json`, {}.toJson.prettyPrint) + HttpEntity(ContentTypes.`application/json`, """{ "message": "No active meetings"}""".parseJson.prettyPrint) } } complete(entityFuture) 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 5e98ab694a..c3a7eca2b7 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 @@ -99,6 +99,7 @@ class MeetingActor( object CheckVoiceRecordingInternalMsg object SyncVoiceUserStatusInternalMsg object MeetingInfoAnalyticsMsg + object MeetingInfoAnalyticsLogMsg override val supervisorStrategy = OneForOneStrategy(maxNrOfRetries = 10, withinTimeRange = 1 minute) { case e: Exception => { @@ -210,21 +211,28 @@ class MeetingActor( CheckVoiceRecordingInternalMsg ) -// TODO: Aggregation of all meetings required, to expose via api and log it -// context.system.scheduler.schedule( -// 1 minute, -// 1 minute, -// self, -// MeetingInfoAnalyticsMsg -// ) + context.system.scheduler.scheduleOnce( + 10 seconds, + self, + MeetingInfoAnalyticsLogMsg + ) + + context.system.scheduler.schedule( + 10 seconds, + 30 seconds, + self, + MeetingInfoAnalyticsMsg + ) def receive = { case SyncVoiceUserStatusInternalMsg => checkVoiceConfUsersStatus() case CheckVoiceRecordingInternalMsg => checkVoiceConfIsRunningAndRecording() - case MeetingInfoAnalyticsMsg => + case MeetingInfoAnalyticsLogMsg => handleMeetingInfoAnalyticsLogging() + case MeetingInfoAnalyticsMsg => + handleMeetingInfoAnalyticsService() case msg: CamStreamSubscribeSysMsg => handleCamStreamSubscribeSysMsg(msg) case msg: ScreenStreamSubscribeSysMsg => @@ -520,12 +528,14 @@ class MeetingActor( private def handleScreenStreamSubscribeSysMsg(msg: ScreenStreamSubscribeSysMsg): Unit = ??? - def handleMeetingInfoAnalyticsLogging(): Unit = { + private def handleMeetingInfoAnalyticsLogging(): Unit = { val meetingInfoAnalyticsLogMsg: MeetingInfoAnalytics = prepareMeetingInfo() - val event = MsgBuilder.buildMeetingInfoAnalyticsMsg(meetingInfoAnalyticsLogMsg) outGW.send(event) + } + private def handleMeetingInfoAnalyticsService(): Unit = { + val meetingInfoAnalyticsLogMsg: MeetingInfoAnalytics = prepareMeetingInfo() val event2 = MsgBuilder.buildMeetingInfoAnalyticsServiceMsg(meetingInfoAnalyticsLogMsg) outGW.send(event2) } diff --git a/akka-bbb-apps/src/main/scala/org/bigbluebutton/service/MeetingInfoService.scala b/akka-bbb-apps/src/main/scala/org/bigbluebutton/service/MeetingInfoService.scala index 507a2fb409..f7af5a95a6 100755 --- a/akka-bbb-apps/src/main/scala/org/bigbluebutton/service/MeetingInfoService.scala +++ b/akka-bbb-apps/src/main/scala/org/bigbluebutton/service/MeetingInfoService.scala @@ -58,7 +58,11 @@ class MeetingInfoActor extends Actor with ActorLogging { override def receive: Receive = { case msg: BbbCommonEnvCoreMsg => handle(msg) case GetMeetingsInfoMessage => - sender ! MeetingInfoListResponseMsg(Option(meetingInfoMap.values.toList)) + if (meetingInfoMap.size > 0) { + sender ! MeetingInfoListResponseMsg(Option(meetingInfoMap.values.toList)) + } else { + sender ! MeetingInfoListResponseMsg(None) + } case GetMeetingInfoMessage(meetingId) => meetingInfoMap.get(meetingId) match { case Some(meetingInfoAnalytics) =>