Merge remote-tracking branch 'bigbluebutton/master' into mobile-no-force-debug

# Conflicts:
#	clients/flash/air-client/src/org/bigbluebutton/air/main/views/LoadingScreenMediator.as
This commit is contained in:
Ghazi Triki 2018-09-13 09:25:40 +01:00
commit 845ffc8343
1468 changed files with 56862 additions and 33791 deletions

View File

@ -0,0 +1,2 @@
Dockerfile

24
akka-bbb-apps/Dockerfile Normal file
View File

@ -0,0 +1,24 @@
FROM bbb-common-message AS builder
ARG COMMON_VERSION=0.0.1-SNAPSHOT
COPY . /source
RUN cd /source \
&& find -name build.sbt -exec sed -i "s|\(.*org.bigbluebutton.*bbb-common-message[^\"]*\"[ ]*%[ ]*\)\"[^\"]*\"\(.*\)|\1\"$COMMON_VERSION\"\2|g" {} \; \
&& sbt compile
RUN apt-get update \
&& apt-get -y install fakeroot
RUN cd /source \
&& sbt debian:packageBin
# FROM ubuntu:16.04
FROM openjdk:8-jre-slim-stretch
COPY --from=builder /source/target/*.deb /root/
RUN dpkg -i /root/*.deb
CMD ["/usr/share/bbb-apps-akka/bin/bbb-apps-akka"]

View File

@ -1,6 +1,6 @@
import com.google.gson.Gson import com.google.gson.Gson
import org.bigbluebutton.messages.BreakoutRoomJoinURL import org.bigbluebutton.messages.BreakoutRoomJoinURL
object BreakoutRoom { object BreakoutRoom {
val gson = new Gson //> gson : com.google.gson.Gson = {serializeNulls:falsefactories:[Factory[typeH val gson = new Gson //> gson : com.google.gson.Gson = {serializeNulls:falsefactories:[Factory[typeH
//| ierarchy=com.google.gson.JsonElement,adapter=com.google.gson.internal.bind.T //| ierarchy=com.google.gson.JsonElement,adapter=com.google.gson.internal.bind.T
@ -16,8 +16,8 @@ object BreakoutRoom {
//| nal.bind.TypeAdapters$11@3ac42916], Factory[type=java.lang.Double+double,ada //| nal.bind.TypeAdapters$11@3ac42916], Factory[type=java.lang.Double+double,ada
//| pter=com.google.gson.Gso //| pter=com.google.gson.Gso
//| Output exceeds cutoff limit. //| Output exceeds cutoff limit.
val string = "{\"payload\":{\"redirectJoinUrl\":\"alink\",\"breakoutMeetingId\":\"4455e780b6f62cd5fcf09367aef62d9bc5108375-1479728671031\",\"noRedirectJoinUrl\":\"http://bbb.riadvice.com/bigbluebutton/api/join?fullName\u003dOpera\u0026isBreakout\u003dtrue\u0026meetingID\u003d4455e780b6f62cd5fcf09367aef62d9bc5108375-1479728671031\u0026password\u003dmp\u0026redirect\u003dfalse\u0026userID\u003d6pt0vfeaxdze_1-1\u0026checksum\u003d51c4a1398b88170c25f1a71521bca604e784ab23\",\"parentMeetingId\":\"183f0bf3a0982a127bdb8161e0c44eb696b3e75c-1479728593178\",\"userId\":\"6pt0vfeaxdze_1\"},\"header\":{\"name\":\"BreakoutRoomJoinURL\",\"version\":\"0.0.1\",\"current_time\":1479728673586,\"timestamp\":8549632}}" val string = "{\"payload\":{\"redirectJoinUrl\":\"alink\",\"breakoutMeetingId\":\"4455e780b6f62cd5fcf09367aef62d9bc5108375-1479728671031\",\"redirectToHtml5JoinUrl\":\"http://bbb.riadvice.com/bigbluebutton/api/join?fullName\u003dOpera\u0026isBreakout\u003dtrue\u0026meetingID\u003d4455e780b6f62cd5fcf09367aef62d9bc5108375-1479728671031\u0026password\u003dmp\u0026redirect\u003dfalse\u0026userID\u003d6pt0vfeaxdze_1-1\u0026checksum\u003d51c4a1398b88170c25f1a71521bca604e784ab23\",\"parentMeetingId\":\"183f0bf3a0982a127bdb8161e0c44eb696b3e75c-1479728593178\",\"userId\":\"6pt0vfeaxdze_1\"},\"header\":{\"name\":\"BreakoutRoomJoinURL\",\"version\":\"0.0.1\",\"current_time\":1479728673586,\"timestamp\":8549632}}"
//> string : String = {"payload":{"redirectJoinUrl":"alink","breakoutMeetingId" //> string : String = {"payload":{"redirectJoinUrl":"alink","breakoutMeetingId"
//| :"4455e780b6f62cd5fcf09367aef62d9bc5108375-1479728671031","noRedirectJoinUrl //| :"4455e780b6f62cd5fcf09367aef62d9bc5108375-1479728671031","noRedirectJoinUrl
//| ":"http://bbb.riadvice.com/bigbluebutton/api/join?fullName=Opera&isBreakout= //| ":"http://bbb.riadvice.com/bigbluebutton/api/join?fullName=Opera&isBreakout=
@ -26,14 +26,14 @@ object BreakoutRoom {
//| 1521bca604e784ab23","parentMeetingId":"183f0bf3a0982a127bdb8161e0c44eb696b3e //| 1521bca604e784ab23","parentMeetingId":"183f0bf3a0982a127bdb8161e0c44eb696b3e
//| 75c-1479728593178","userId":"6pt0vfeaxdze_1"},"header":{"name":"BreakoutRoom //| 75c-1479728593178","userId":"6pt0vfeaxdze_1"},"header":{"name":"BreakoutRoom
//| JoinURL","version":"0.0.1","current_time":1479728673586,"timestamp":8549632} //| JoinURL","version":"0.0.1","current_time":1479728673586,"timestamp":8549632}
//| } //| }
val brjum: BreakoutRoomJoinURL = gson.fromJson(string, classOf[BreakoutRoomJoinURL]) val brjum: BreakoutRoomJoinURL = gson.fromJson(string, classOf[BreakoutRoomJoinURL])
//> brjum : org.bigbluebutton.messages.BreakoutRoomJoinURL = org.bigbluebutton. //> brjum : org.bigbluebutton.messages.BreakoutRoomJoinURL = org.bigbluebutton.
//| messages.BreakoutRoomJoinURL@3327bd23 //| messages.BreakoutRoomJoinURL@3327bd23
println(brjum.payload.userId) //> 6pt0vfeaxdze_1 println(brjum.payload.userId) //> 6pt0vfeaxdze_1
println(brjum.payload.parentMeetingId) //> 183f0bf3a0982a127bdb8161e0c44eb696b3e75c-1479728593178 println(brjum.payload.parentMeetingId) //> 183f0bf3a0982a127bdb8161e0c44eb696b3e75c-1479728593178
println(brjum.payload.breakoutMeetingId) //> 4455e780b6f62cd5fcf09367aef62d9bc5108375-1479728671031 println(brjum.payload.breakoutMeetingId) //> 4455e780b6f62cd5fcf09367aef62d9bc5108375-1479728671031
println(brjum.payload.redirectJoinURL) //> null println(brjum.payload.redirectJoinURL) //> null
println(brjum.payload.noRedirectJoinURL) //> null println(brjum.payload.redirectToHtml5JoinURL) //> null
} }

View File

@ -35,13 +35,6 @@ redis {
keyExpiry=1209600 keyExpiry=1209600
} }
inactivity {
# time in seconds
deadline=7200
# inactivity warning message
timeLeft=300
}
expire { expire {
# time in seconds # time in seconds
lastUserLeft = 60 lastUserLeft = 60
@ -82,6 +75,8 @@ services {
apps { apps {
checkPermissions = true checkPermissions = true
endMeetingWhenNoMoreAuthedUsers = false
endMeetingWhenNoMoreAuthedUsersAfterMinutes = 2
} }
voiceConf { voiceConf {
@ -90,4 +85,9 @@ voiceConf {
recording { recording {
chapterBreakLengthInMinutes = 180 chapterBreakLengthInMinutes = 180
} }
whiteboard {
multiUserDefault = false
}

View File

@ -1,20 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<configuration> <configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder> <layout class="ch.qos.logback.classic.PatternLayout">
<pattern>%date{ISO8601} %-5level %logger{36} - %msg%n</pattern> <Pattern>%d{"yyyy-MM-dd'T'HH:mm:ss.SSSXXX"} %-5level %logger{35} - %msg%n</Pattern>
</encoder> </layout>
</appender> </appender>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<File>logs/bbb-apps-akka.log</File> <File>logs/bbb-apps-akka.log</File>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<FileNamePattern>logs/bbb-apps-akka.%d{yyyy-MM-dd}.log</FileNamePattern> <FileNamePattern>logs/bbb-apps-akka.%d{yyyy-MM-dd}.log</FileNamePattern>
<!-- keep 30 days worth of history --> <!-- keep 14 days worth of history -->
<MaxHistory>5</MaxHistory> <MaxHistory>14</MaxHistory>
</rollingPolicy> </rollingPolicy>
<layout class="ch.qos.logback.classic.PatternLayout"> <layout class="ch.qos.logback.classic.PatternLayout">
<Pattern>%d{"yyyy-MM-dd HH:mm:ss,SSSXXX"} [%thread] %-5level %logger{35} - %msg%n</Pattern> <Pattern>%d{"yyyy-MM-dd'T'HH:mm:ss.SSSXXX"} %-5level %logger{35} - %msg%n</Pattern>
</layout> </layout>
</appender> </appender>
@ -25,4 +25,4 @@
<appender-ref ref="STDOUT"/> <appender-ref ref="STDOUT"/>
<appender-ref ref="FILE" /> <appender-ref ref="FILE" />
</root> </root>
</configuration> </configuration>

View File

@ -21,9 +21,6 @@ trait SystemConfiguration {
lazy val red5DeskShareIP = Try(config.getString("red5.deskshareip")).getOrElse("127.0.0.1") lazy val red5DeskShareIP = Try(config.getString("red5.deskshareip")).getOrElse("127.0.0.1")
lazy val red5DeskShareApp = Try(config.getString("red5.deskshareapp")).getOrElse("") lazy val red5DeskShareApp = Try(config.getString("red5.deskshareapp")).getOrElse("")
lazy val inactivityDeadline = Try(config.getInt("inactivity.deadline")).getOrElse(2 * 3600) // 2 hours
lazy val inactivityTimeLeft = Try(config.getInt("inactivity.timeLeft")).getOrElse(5 * 60) // 5 minutes
lazy val expireLastUserLeft = Try(config.getInt("expire.lastUserLeft")).getOrElse(60) // 1 minute lazy val expireLastUserLeft = Try(config.getInt("expire.lastUserLeft")).getOrElse(60) // 1 minute
lazy val expireNeverJoined = Try(config.getInt("expire.neverJoined")).getOrElse(5 * 60) // 5 minutes lazy val expireNeverJoined = Try(config.getInt("expire.neverJoined")).getOrElse(5 * 60) // 5 minutes
@ -63,4 +60,9 @@ trait SystemConfiguration {
lazy val voiceConfRecordPath = Try(config.getString("voiceConf.recordPath")).getOrElse("/var/freeswitch/meetings") lazy val voiceConfRecordPath = Try(config.getString("voiceConf.recordPath")).getOrElse("/var/freeswitch/meetings")
lazy val recordingChapterBreakLenghtInMinutes = Try(config.getInt("recording.chapterBreakLengthInMinutes")).getOrElse(180) lazy val recordingChapterBreakLenghtInMinutes = Try(config.getInt("recording.chapterBreakLengthInMinutes")).getOrElse(180)
lazy val endMeetingWhenNoMoreAuthedUsers = Try(config.getBoolean("apps.endMeetingWhenNoMoreAuthedUsers")).getOrElse(false)
lazy val endMeetingWhenNoMoreAuthedUsersAfterMinutes = Try(config.getInt("apps.endMeetingWhenNoMoreAuthedUsersAfterMinutes")).getOrElse(2)
lazy val multiUserWhiteboardDefault = Try(config.getBoolean("whiteboard.multiUserDefault")).getOrElse(false)
} }

View File

@ -26,6 +26,8 @@ case class MonitorNumberOfUsersInternalMsg(meetingID: String) extends InMessage
*/ */
case class SendTimeRemainingAuditInternalMsg(meetingId: String) extends InMessage case class SendTimeRemainingAuditInternalMsg(meetingId: String) extends InMessage
case class SendRecordingTimerInternalMsg(meetingId: String) extends InMessage
case class ExtendMeetingDuration(meetingId: String, userId: String) extends InMessage case class ExtendMeetingDuration(meetingId: String, userId: String) extends InMessage
case class DestroyMeetingInternalMsg(meetingId: String) extends InMessage case class DestroyMeetingInternalMsg(meetingId: String) extends InMessage

View File

@ -9,10 +9,11 @@ object BreakoutModel {
externalId: String, externalId: String,
name: String, name: String,
sequence: Integer, sequence: Integer,
freeJoin: Boolean,
voiceConf: String, voiceConf: String,
assignedUsers: Vector[String] assignedUsers: Vector[String]
): BreakoutRoom2x = { ): BreakoutRoom2x = {
new BreakoutRoom2x(id, externalId, name, parentId, sequence, voiceConf, assignedUsers, Vector(), Vector(), None, false) new BreakoutRoom2x(id, externalId, name, parentId, sequence, freeJoin, voiceConf, assignedUsers, Vector(), Vector(), None, false)
} }
} }

View File

@ -7,8 +7,9 @@ import scala.collection.immutable.HashMap
import scala.collection.JavaConverters._ import scala.collection.JavaConverters._
import org.bigbluebutton.common2.msgs.AnnotationVO import org.bigbluebutton.common2.msgs.AnnotationVO
import org.bigbluebutton.core.apps.whiteboard.Whiteboard import org.bigbluebutton.core.apps.whiteboard.Whiteboard
import org.bigbluebutton.SystemConfiguration
class WhiteboardModel { class WhiteboardModel extends SystemConfiguration {
private var _whiteboards = new HashMap[String, Whiteboard]() private var _whiteboards = new HashMap[String, Whiteboard]()
private def saveWhiteboard(wb: Whiteboard) { private def saveWhiteboard(wb: Whiteboard) {
@ -24,7 +25,7 @@ class WhiteboardModel {
} }
private def createWhiteboard(wbId: String): Whiteboard = { private def createWhiteboard(wbId: String): Whiteboard = {
new Whiteboard(wbId, false, System.currentTimeMillis(), 0, new HashMap[String, List[AnnotationVO]]()) new Whiteboard(wbId, multiUserWhiteboardDefault, System.currentTimeMillis(), 0, new HashMap[String, List[AnnotationVO]]())
} }
private def getAnnotationsByUserId(wb: Whiteboard, id: String): List[AnnotationVO] = { private def getAnnotationsByUserId(wb: Whiteboard, id: String): List[AnnotationVO] = {

View File

@ -56,10 +56,11 @@ object BreakoutRoomsUtil {
"userID" -> urlEncode(userId), "userID" -> urlEncode(userId),
"isBreakout" -> urlEncode(isBreakout.toString()), "isBreakout" -> urlEncode(isBreakout.toString()),
"meetingID" -> urlEncode(breakoutMeetingId), "meetingID" -> urlEncode(breakoutMeetingId),
"password" -> urlEncode(password) "password" -> urlEncode(password),
"redirect" -> urlEncode("true")
) )
(params + ("redirect" -> urlEncode("true")), params + ("redirect" -> urlEncode("false"))) (params, params + ("joinViaHtml5" -> urlEncode("true")))
} }
def sortParams(params: collection.immutable.Map[String, String]): SortedSet[String] = { def sortParams(params: collection.immutable.Map[String, String]): SortedSet[String] = {

View File

@ -17,37 +17,37 @@ trait BreakoutHdlrHelpers extends SystemConfiguration {
for { for {
user <- Users2x.findWithIntId(liveMeeting.users2x, userId) user <- Users2x.findWithIntId(liveMeeting.users2x, userId)
apiCall = "join" apiCall = "join"
(redirectParams, noRedirectParams) = BreakoutRoomsUtil.joinParams(user.name, userId + "-" + roomSequence, true, (redirectParams, redirectToHtml5Params) = BreakoutRoomsUtil.joinParams(user.name, userId + "-" + roomSequence, true,
externalMeetingId, liveMeeting.props.password.moderatorPass) externalMeetingId, liveMeeting.props.password.moderatorPass)
// We generate a first url with redirect -> true // We generate a first url with redirect -> true
redirectBaseString = BreakoutRoomsUtil.createBaseString(redirectParams) redirectBaseString = BreakoutRoomsUtil.createBaseString(redirectParams)
redirectJoinURL = BreakoutRoomsUtil.createJoinURL(bbbWebAPI, apiCall, redirectBaseString, redirectJoinURL = BreakoutRoomsUtil.createJoinURL(bbbWebAPI, apiCall, redirectBaseString,
BreakoutRoomsUtil.calculateChecksum(apiCall, redirectBaseString, bbbWebSharedSecret)) BreakoutRoomsUtil.calculateChecksum(apiCall, redirectBaseString, bbbWebSharedSecret))
// We generate a second url with redirect -> false // We generate a second url with redirect -> true and joinViaHtml5 -> true
noRedirectBaseString = BreakoutRoomsUtil.createBaseString(noRedirectParams) redirectToHtml5BaseString = BreakoutRoomsUtil.createBaseString(redirectToHtml5Params)
noRedirectJoinURL = BreakoutRoomsUtil.createJoinURL(bbbWebAPI, apiCall, noRedirectBaseString, redirectToHtml5JoinURL = BreakoutRoomsUtil.createJoinURL(bbbWebAPI, apiCall, redirectToHtml5BaseString,
BreakoutRoomsUtil.calculateChecksum(apiCall, noRedirectBaseString, bbbWebSharedSecret)) BreakoutRoomsUtil.calculateChecksum(apiCall, redirectToHtml5BaseString, bbbWebSharedSecret))
} yield { } yield {
sendJoinURLMsg(liveMeeting.props.meetingProp.intId, breakoutId, externalMeetingId, sendJoinURLMsg(liveMeeting.props.meetingProp.intId, breakoutId, externalMeetingId,
userId, redirectJoinURL, noRedirectJoinURL) userId, redirectJoinURL, redirectToHtml5JoinURL)
} }
} }
def sendJoinURLMsg(meetingId: String, breakoutId: String, externalId: String, def sendJoinURLMsg(meetingId: String, breakoutId: String, externalId: String,
userId: String, redirectJoinURL: String, noRedirectJoinURL: String): Unit = { userId: String, redirectJoinURL: String, redirectToHtml5JoinURL: String): Unit = {
def build(meetingId: String, breakoutId: String, def build(meetingId: String, breakoutId: String,
userId: String, redirectJoinURL: String, noRedirectJoinURL: String): BbbCommonEnvCoreMsg = { userId: String, redirectJoinURL: String, redirectToHtml5JoinURL: String): BbbCommonEnvCoreMsg = {
val routing = Routing.addMsgToClientRouting(MessageTypes.DIRECT, meetingId, userId) val routing = Routing.addMsgToClientRouting(MessageTypes.DIRECT, meetingId, userId)
val envelope = BbbCoreEnvelope(BreakoutRoomJoinURLEvtMsg.NAME, routing) val envelope = BbbCoreEnvelope(BreakoutRoomJoinURLEvtMsg.NAME, routing)
val header = BbbClientMsgHeader(BreakoutRoomJoinURLEvtMsg.NAME, meetingId, userId) val header = BbbClientMsgHeader(BreakoutRoomJoinURLEvtMsg.NAME, meetingId, userId)
val body = BreakoutRoomJoinURLEvtMsgBody(meetingId, breakoutId, externalId, val body = BreakoutRoomJoinURLEvtMsgBody(meetingId, breakoutId, externalId,
userId, redirectJoinURL, noRedirectJoinURL) userId, redirectJoinURL, redirectToHtml5JoinURL)
val event = BreakoutRoomJoinURLEvtMsg(header, body) val event = BreakoutRoomJoinURLEvtMsg(header, body)
BbbCommonEnvCoreMsg(envelope, event) BbbCommonEnvCoreMsg(envelope, event)
} }
val msgEvent = build(meetingId, breakoutId, userId, redirectJoinURL, noRedirectJoinURL) val msgEvent = build(meetingId, breakoutId, userId, redirectJoinURL, redirectToHtml5JoinURL)
outGW.send(msgEvent) outGW.send(msgEvent)
} }

View File

@ -61,7 +61,7 @@ trait BreakoutRoomCreatedMsgHdlr extends BreakoutHdlrHelpers {
def sendBreakoutRoomsList(breakoutModel: BreakoutModel): BreakoutModel = { def sendBreakoutRoomsList(breakoutModel: BreakoutModel): BreakoutModel = {
val breakoutRooms = breakoutModel.rooms.values.toVector map { r => val breakoutRooms = breakoutModel.rooms.values.toVector map { r =>
new BreakoutRoomInfo(r.name, r.externalId, r.id, r.sequence) new BreakoutRoomInfo(r.name, r.externalId, r.id, r.sequence, r.freeJoin)
} }
log.info("Sending breakout rooms list to {} with containing {} room(s)", liveMeeting.props.meetingProp.intId, breakoutRooms.length) log.info("Sending breakout rooms list to {} with containing {} room(s)", liveMeeting.props.meetingProp.intId, breakoutRooms.length)
@ -88,7 +88,7 @@ trait BreakoutRoomCreatedMsgHdlr extends BreakoutHdlrHelpers {
BbbCommonEnvCoreMsg(envelope, event) BbbCommonEnvCoreMsg(envelope, event)
} }
val breakoutInfo = BreakoutRoomInfo(room.name, room.externalId, room.id, room.sequence) val breakoutInfo = BreakoutRoomInfo(room.name, room.externalId, room.id, room.sequence, room.freeJoin)
val event = build(liveMeeting.props.meetingProp.intId, breakoutInfo) val event = build(liveMeeting.props.meetingProp.intId, breakoutInfo)
outGW.send(event) outGW.send(event)

View File

@ -28,7 +28,7 @@ trait BreakoutRoomsListMsgHdlr {
breakoutModel <- state.breakout breakoutModel <- state.breakout
} yield { } yield {
val rooms = breakoutModel.rooms.values.toVector map { r => val rooms = breakoutModel.rooms.values.toVector map { r =>
new BreakoutRoomInfo(r.name, r.externalId, r.id, r.sequence) new BreakoutRoomInfo(r.name, r.externalId, r.id, r.sequence, r.freeJoin)
} }
val ready = breakoutModel.hasAllStarted() val ready = breakoutModel.hasAllStarted()
broadcastEvent(rooms, ready) broadcastEvent(rooms, ready)

View File

@ -46,7 +46,7 @@ trait CreateBreakoutRoomsCmdMsgHdlr extends RightsManagementTrait {
val (internalId, externalId) = BreakoutRoomsUtil.createMeetingIds(liveMeeting.props.meetingProp.intId, i) val (internalId, externalId) = BreakoutRoomsUtil.createMeetingIds(liveMeeting.props.meetingProp.intId, i)
val voiceConf = BreakoutRoomsUtil.createVoiceConfId(liveMeeting.props.voiceProp.voiceConf, i) val voiceConf = BreakoutRoomsUtil.createVoiceConfId(liveMeeting.props.voiceProp.voiceConf, i)
val breakout = BreakoutModel.create(parentId, internalId, externalId, room.name, room.sequence, voiceConf, room.users) val breakout = BreakoutModel.create(parentId, internalId, externalId, room.name, room.sequence, room.freeJoin, voiceConf, room.users)
rooms = rooms + (breakout.id -> breakout) rooms = rooms + (breakout.id -> breakout)
} }
@ -55,6 +55,7 @@ trait CreateBreakoutRoomsCmdMsgHdlr extends RightsManagementTrait {
breakout.id, breakout.name, breakout.id, breakout.name,
liveMeeting.props.meetingProp.intId, liveMeeting.props.meetingProp.intId,
breakout.sequence, breakout.sequence,
breakout.freeJoin,
breakout.voiceConf, breakout.voiceConf,
msg.body.durationInMinutes, msg.body.durationInMinutes,
liveMeeting.props.password.moderatorPass, liveMeeting.props.password.moderatorPass,

View File

@ -6,6 +6,7 @@ class ChatApp2x(implicit val context: ActorContext)
extends GetChatHistoryReqMsgHdlr extends GetChatHistoryReqMsgHdlr
with SendPublicMessagePubMsgHdlr with SendPublicMessagePubMsgHdlr
with SendPrivateMessagePubMsgHdlr with SendPrivateMessagePubMsgHdlr
with ClearPublicChatHistoryPubMsgHdlr { with ClearPublicChatHistoryPubMsgHdlr
with UserTypingPubMsgHdlr {
} }

View File

@ -0,0 +1,21 @@
package org.bigbluebutton.core.apps.chat
import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.bus.MessageBus
import org.bigbluebutton.core.running.{ LiveMeeting, LogHelper }
trait UserTypingPubMsgHdlr extends LogHelper {
def handle(msg: UserTypingPubMsg, liveMeeting: LiveMeeting, bus: MessageBus): Unit = {
def broadcastEvent(msg: UserTypingPubMsg): Unit = {
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, liveMeeting.props.meetingProp.intId, msg.header.userId)
val envelope = BbbCoreEnvelope(UserTypingEvtMsg.NAME, routing)
val header = BbbClientMsgHeader(UserTypingEvtMsg.NAME, liveMeeting.props.meetingProp.intId, msg.header.userId)
val body = UserTypingEvtMsgBody(msg.body.chatId, msg.header.userId)
val event = UserTypingEvtMsg(header, body)
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
bus.outGW.send(msgEvent)
}
broadcastEvent(msg)
}
}

View File

@ -0,0 +1,38 @@
package org.bigbluebutton.core.apps.groupchats
import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.bus.MessageBus
import org.bigbluebutton.core.domain.MeetingState2x
import org.bigbluebutton.core.models.GroupChat
import org.bigbluebutton.core.running.LiveMeeting
import org.bigbluebutton.core.models.SystemUser
trait CreateDefaultPublicGroupChat {
this: GroupChatHdlrs =>
def handleCreateDefaultPublicGroupChat(state: MeetingState2x, liveMeeting: LiveMeeting, bus: MessageBus): MeetingState2x = {
val groupChat: GroupChat = GroupChatApp.createDefaultPublicGroupChat()
def buildGroupChatCreatedEvtMsg(meetingId: String, userId: String, gc: GroupChat): BbbCommonEnvCoreMsg = {
val correlationId = "SYSTEM-" + System.currentTimeMillis()
val msgs = gc.msgs.map(m => GroupChatApp.toMessageToUser(m))
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, userId)
val envelope = BbbCoreEnvelope(GroupChatCreatedEvtMsg.NAME, routing)
val header = BbbClientMsgHeader(GroupChatCreatedEvtMsg.NAME, meetingId, userId)
val body = GroupChatCreatedEvtMsgBody(correlationId, gc.id, gc.createdBy, gc.name, gc.access, gc.users, msgs)
val event = GroupChatCreatedEvtMsg(header, body)
BbbCommonEnvCoreMsg(envelope, event)
}
val respMsg = buildGroupChatCreatedEvtMsg(
liveMeeting.props.meetingProp.intId,
SystemUser.ID,
groupChat
)
bus.outGW.send(respMsg)
val groupChats = state.groupChats.add(groupChat)
state.update(groupChats)
}
}

View File

@ -44,12 +44,9 @@ object GroupChatApp {
} }
} }
def createDefaultPublicGroupChat(chatId: String, state: MeetingState2x): MeetingState2x = { def createDefaultPublicGroupChat(): GroupChat = {
val createBy = GroupChatUser(SystemUser.ID, SystemUser.ID) val createBy = GroupChatUser(SystemUser.ID, SystemUser.ID)
val defaultPubGroupChat = GroupChatFactory.create(chatId, chatId, GroupChatFactory.create(MAIN_PUBLIC_CHAT, MAIN_PUBLIC_CHAT, GroupChatAccess.PUBLIC, createBy, Vector.empty, Vector.empty)
GroupChatAccess.PUBLIC, createBy, Vector.empty, Vector.empty)
val groupChats = state.groupChats.add(defaultPubGroupChat)
state.update(groupChats)
} }
def createTestPublicGroupChat(state: MeetingState2x): MeetingState2x = { def createTestPublicGroupChat(state: MeetingState2x): MeetingState2x = {
@ -60,6 +57,10 @@ object GroupChatApp {
state.update(groupChats) state.update(groupChats)
} }
def getAllGroupChatsInMeeting(state: MeetingState2x): Vector[GroupChat] = {
state.groupChats.getAllGroupChatsInMeeting()
}
def genTestChatMsgHistory(chatId: String, state: MeetingState2x, userId: String, liveMeeting: LiveMeeting): MeetingState2x = { def genTestChatMsgHistory(chatId: String, state: MeetingState2x, userId: String, liveMeeting: LiveMeeting): MeetingState2x = {
def addH(state: MeetingState2x, userId: String, liveMeeting: LiveMeeting, msg: GroupChatMsgFromUser): MeetingState2x = { def addH(state: MeetingState2x, userId: String, liveMeeting: LiveMeeting, msg: GroupChatMsgFromUser): MeetingState2x = {
val newState = for { val newState = for {

View File

@ -5,9 +5,11 @@ import akka.event.Logging
class GroupChatHdlrs(implicit val context: ActorContext) class GroupChatHdlrs(implicit val context: ActorContext)
extends CreateGroupChatReqMsgHdlr extends CreateGroupChatReqMsgHdlr
with CreateDefaultPublicGroupChat
with GetGroupChatMsgsReqMsgHdlr with GetGroupChatMsgsReqMsgHdlr
with GetGroupChatsReqMsgHdlr with GetGroupChatsReqMsgHdlr
with SendGroupChatMessageMsgHdlr { with SendGroupChatMessageMsgHdlr
with SyncGetGroupChatsInfoMsgHdlr {
val log = Logging(context.system, getClass) val log = Logging(context.system, getClass)
} }

View File

@ -0,0 +1,53 @@
package org.bigbluebutton.core.apps.groupchats
import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.bus.MessageBus
import org.bigbluebutton.core.domain.MeetingState2x
import org.bigbluebutton.core.running.LiveMeeting
trait SyncGetGroupChatsInfoMsgHdlr {
this: GroupChatHdlrs =>
def handleSyncGetGroupChatsInfo(state: MeetingState2x, liveMeeting: LiveMeeting, bus: MessageBus): MeetingState2x = {
def buildSyncGetGroupChatsRespMsg(allChats: Vector[GroupChatInfo]): BbbCommonEnvCoreMsg = {
val routing = Routing.addMsgToClientRouting(MessageTypes.DIRECT, liveMeeting.props.meetingProp.intId, "nodeJSapp")
val envelope = BbbCoreEnvelope(SyncGetGroupChatsRespMsg.NAME, routing)
val header = BbbClientMsgHeader(SyncGetGroupChatsRespMsg.NAME, liveMeeting.props.meetingProp.intId, "nodeJSapp")
val body = SyncGetGroupChatsRespMsgBody(allChats)
val event = SyncGetGroupChatsRespMsg(header, body)
BbbCommonEnvCoreMsg(envelope, event)
}
def buildSyncGetGroupChatMsgsRespMsg(msgs: Vector[GroupChatMsgToUser], chatId: String): BbbCommonEnvCoreMsg = {
val routing = Routing.addMsgToClientRouting(MessageTypes.DIRECT, liveMeeting.props.meetingProp.intId, "nodeJSapp")
val envelope = BbbCoreEnvelope(SyncGetGroupChatMsgsRespMsg.NAME, routing)
val header = BbbClientMsgHeader(SyncGetGroupChatMsgsRespMsg.NAME, liveMeeting.props.meetingProp.intId, "nodeJSapp")
val body = SyncGetGroupChatMsgsRespMsgBody(chatId, msgs)
val event = SyncGetGroupChatMsgsRespMsg(header, body)
BbbCommonEnvCoreMsg(envelope, event)
}
// fetching all the group chats in the meeting
val chats = GroupChatApp.getAllGroupChatsInMeeting(state)
// mapping group chats, while fetching and publishing messages for each group chat
val allChats = chats map (pc => {
val msgs = pc.msgs.toVector map (m => GroupChatMsgToUser(m.id, m.createdOn, m.correlationId,
m.sender, m.color, m.message))
val respMsg = buildSyncGetGroupChatMsgsRespMsg(msgs, pc.id)
bus.outGW.send(respMsg)
GroupChatInfo(pc.id, pc.name, pc.access, pc.createdBy, pc.users)
})
// publishing a message with the group chat info
val respMsg = buildSyncGetGroupChatsRespMsg(allChats)
bus.outGW.send(respMsg)
state
}
}

View File

@ -5,6 +5,7 @@ import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.bus.MessageBus import org.bigbluebutton.core.bus.MessageBus
import org.bigbluebutton.core.models.Polls import org.bigbluebutton.core.models.Polls
import org.bigbluebutton.core.running.{ LiveMeeting } import org.bigbluebutton.core.running.{ LiveMeeting }
import org.bigbluebutton.core.models.Users2x
trait RespondToPollReqMsgHdlr { trait RespondToPollReqMsgHdlr {
this: PollApp2x => this: PollApp2x =>
@ -34,12 +35,29 @@ trait RespondToPollReqMsgHdlr {
bus.outGW.send(msgEvent) bus.outGW.send(msgEvent)
} }
def broadcastUserRespondedToPollRespMsg(msg: RespondToPollReqMsg, pollId: String, answerId: Int, sendToId: String): Unit = {
val routing = Routing.addMsgToClientRouting(MessageTypes.DIRECT, liveMeeting.props.meetingProp.intId, sendToId)
val envelope = BbbCoreEnvelope(UserRespondedToPollRespMsg.NAME, routing)
val header = BbbClientMsgHeader(UserRespondedToPollRespMsg.NAME, liveMeeting.props.meetingProp.intId, sendToId)
val body = UserRespondedToPollRespMsgBody(pollId, msg.header.userId, answerId)
val event = UserRespondedToPollRespMsg(header, body)
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
bus.outGW.send(msgEvent)
}
for { for {
(pollId: String, updatedPoll: SimplePollResultOutVO) <- Polls.handleRespondToPollReqMsg(msg.header.userId, msg.body.pollId, (pollId: String, updatedPoll: SimplePollResultOutVO) <- Polls.handleRespondToPollReqMsg(msg.header.userId, msg.body.pollId,
msg.body.questionId, msg.body.answerId, liveMeeting) msg.body.questionId, msg.body.answerId, liveMeeting)
} yield { } yield {
broadcastPollUpdatedEvent(msg, pollId, updatedPoll) broadcastPollUpdatedEvent(msg, pollId, updatedPoll)
broadcastUserRespondedToPollRecordMsg(msg, pollId, msg.body.answerId) broadcastUserRespondedToPollRecordMsg(msg, pollId, msg.body.answerId)
for {
presenter <- Users2x.findPresenter(liveMeeting.users2x)
} yield {
broadcastUserRespondedToPollRespMsg(msg, pollId, msg.body.answerId, presenter.intId)
}
} }
} }
} }

View File

@ -0,0 +1,27 @@
package org.bigbluebutton.core.apps.presentationpod
import org.bigbluebutton.core.bus.MessageBus
import org.bigbluebutton.core.domain.MeetingState2x
import org.bigbluebutton.core.models.PresentationPod
import org.bigbluebutton.core2.message.senders.MsgBuilder
import org.bigbluebutton.core.running.LiveMeeting
import org.bigbluebutton.core.models.SystemUser
trait CreateDefaultPresentationPod {
this: PresentationPodHdlrs =>
def handleCreateDefaultPresentationPod(state: MeetingState2x, liveMeeting: LiveMeeting, bus: MessageBus): MeetingState2x = {
val SYSTEM_ID = SystemUser.ID
val resultPod: PresentationPod = PresentationPodsApp.createDefaultPresentationPod()
val respMsg = MsgBuilder.buildCreateNewPresentationPodEvtMsg(
liveMeeting.props.meetingProp.intId,
resultPod.currentPresenter,
resultPod.id,
SYSTEM_ID
)
bus.outGW.send(respMsg)
val pods = state.presentationPodManager.addPod(resultPod)
state.update(pods)
}
}

View File

@ -6,6 +6,7 @@ import org.bigbluebutton.core.bus.MessageBus
import org.bigbluebutton.core.domain.MeetingState2x import org.bigbluebutton.core.domain.MeetingState2x
import org.bigbluebutton.core.models.PresentationPod import org.bigbluebutton.core.models.PresentationPod
import org.bigbluebutton.core.running.LiveMeeting import org.bigbluebutton.core.running.LiveMeeting
import org.bigbluebutton.core2.message.senders.MsgBuilder
trait CreateNewPresentationPodPubMsgHdlr extends RightsManagementTrait { trait CreateNewPresentationPodPubMsgHdlr extends RightsManagementTrait {
this: PresentationPodHdlrs => this: PresentationPodHdlrs =>
@ -22,22 +23,14 @@ trait CreateNewPresentationPodPubMsgHdlr extends RightsManagementTrait {
PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, bus.outGW, liveMeeting) PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, bus.outGW, liveMeeting)
state state
} else { } else {
def buildCreateNewPresentationPodEvtMsg(meetingId: String, currentPresenterId: String, podId: String): BbbCommonEnvCoreMsg = {
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, msg.header.userId)
val envelope = BbbCoreEnvelope(CreateNewPresentationPodEvtMsg.NAME, routing)
val header = BbbClientMsgHeader(CreateNewPresentationPodEvtMsg.NAME, meetingId, msg.header.userId)
val body = CreateNewPresentationPodEvtMsgBody(currentPresenterId, podId)
val event = CreateNewPresentationPodEvtMsg(header, body)
BbbCommonEnvCoreMsg(envelope, event)
}
val resultPod: PresentationPod = PresentationPodsApp.createPresentationPod(msg.header.userId) val resultPod: PresentationPod = PresentationPodsApp.createPresentationPod(msg.header.userId)
val respMsg = buildCreateNewPresentationPodEvtMsg( val respMsg = MsgBuilder.buildCreateNewPresentationPodEvtMsg(
liveMeeting.props.meetingProp.intId, liveMeeting.props.meetingProp.intId,
resultPod.currentPresenter, resultPod.id resultPod.currentPresenter,
resultPod.id,
msg.header.userId
) )
bus.outGW.send(respMsg) bus.outGW.send(respMsg)

View File

@ -16,20 +16,7 @@ trait PresentationConversionCompletedSysPubMsgHdlr {
liveMeeting: LiveMeeting, bus: MessageBus liveMeeting: LiveMeeting, bus: MessageBus
): MeetingState2x = { ): MeetingState2x = {
def broadcastPresentationConversionCompletedEvtMsg(podId: String, userId: String, messageKey: String, val meetingId = liveMeeting.props.meetingProp.intId
code: String, presentation: PresentationVO): Unit = {
val routing = Routing.addMsgToClientRouting(
MessageTypes.BROADCAST_TO_MEETING,
liveMeeting.props.meetingProp.intId, userId
)
val envelope = BbbCoreEnvelope(PresentationConversionCompletedEvtMsg.NAME, routing)
val header = BbbClientMsgHeader(PresentationConversionCompletedEvtMsg.NAME, liveMeeting.props.meetingProp.intId, userId)
val body = PresentationConversionCompletedEvtMsgBody(podId, messageKey, code, presentation)
val event = PresentationConversionCompletedEvtMsg(header, body)
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
bus.outGW.send(msgEvent)
}
val pages = new collection.mutable.HashMap[String, PageVO] val pages = new collection.mutable.HashMap[String, PageVO]
@ -39,15 +26,20 @@ trait PresentationConversionCompletedSysPubMsgHdlr {
pages += page.id -> page pages += page.id -> page
} }
val pres = new PresentationInPod(msg.body.presentation.id, msg.body.presentation.name, msg.body.presentation.current, val downloadable = msg.body.presentation.downloadable
pages.toMap, msg.body.presentation.downloadable) val presentationId = msg.body.presentation.id
val pres = new PresentationInPod(presentationId, msg.body.presentation.name, msg.body.presentation.current,
pages.toMap, downloadable)
val presVO = PresentationPodsApp.translatePresentationToPresentationVO(pres) val presVO = PresentationPodsApp.translatePresentationToPresentationVO(pres)
val podId = msg.body.podId val podId = msg.body.podId
val newState = for { val newState = for {
pod <- PresentationPodsApp.getPresentationPod(state, podId) pod <- PresentationPodsApp.getPresentationPod(state, podId)
} yield { } yield {
broadcastPresentationConversionCompletedEvtMsg(pod.id, msg.header.userId, msg.body.messageKey, msg.body.code, presVO) PresentationSender.broadcastPresentationConversionCompletedEvtMsg(bus, meetingId,
pod.id, msg.header.userId, msg.body.messageKey, msg.body.code, presVO)
PresentationSender.broadcastSetPresentationDownloadableEvtMsg(bus, meetingId, pod.id,
msg.header.userId, presentationId, downloadable, pres.name)
var pods = state.presentationPodManager.addPod(pod) var pods = state.presentationPodManager.addPod(pod)
pods = pods.addPresentationToPod(pod.id, pres) pods = pods.addPresentationToPod(pod.id, pres)

View File

@ -5,6 +5,7 @@ import akka.event.Logging
class PresentationPodHdlrs(implicit val context: ActorContext) class PresentationPodHdlrs(implicit val context: ActorContext)
extends CreateNewPresentationPodPubMsgHdlr extends CreateNewPresentationPodPubMsgHdlr
with CreateDefaultPresentationPod
with GetAllPresentationPodsReqMsgHdlr with GetAllPresentationPodsReqMsgHdlr
with SetCurrentPresentationPubMsgHdlr with SetCurrentPresentationPubMsgHdlr
with PresentationConversionCompletedSysPubMsgHdlr with PresentationConversionCompletedSysPubMsgHdlr

View File

@ -11,9 +11,8 @@ object PresentationPodsApp {
PresentationPodFactory.create(creatorId) PresentationPodFactory.create(creatorId)
} }
def createDefaultPresentationPod(state: MeetingState2x): MeetingState2x = { def createDefaultPresentationPod(): PresentationPod = {
val podManager = state.presentationPodManager.addPod(PresentationPodFactory.createDefaultPod()) PresentationPodFactory.createDefaultPod()
state.update(podManager)
} }
def removePresentationPod(state: MeetingState2x, podId: String): MeetingState2x = { def removePresentationPod(state: MeetingState2x, podId: String): MeetingState2x = {

View File

@ -0,0 +1,47 @@
package org.bigbluebutton.core.apps.presentationpod
import org.bigbluebutton.common2.domain.PresentationVO
import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.bus.MessageBus
object PresentationSender {
def broadcastSetPresentationDownloadableEvtMsg(
bus: MessageBus,
meetingId: String,
podId: String, userId: String,
presentationId: String,
downloadable: Boolean,
presFilename: String
): Unit = {
val routing = Routing.addMsgToClientRouting(
MessageTypes.BROADCAST_TO_MEETING,
meetingId, userId
)
val envelope = BbbCoreEnvelope(SetPresentationDownloadableEvtMsg.NAME, routing)
val header = BbbClientMsgHeader(SetPresentationDownloadableEvtMsg.NAME, meetingId, userId)
val body = SetPresentationDownloadableEvtMsgBody(podId, presentationId, downloadable, presFilename)
val event = SetPresentationDownloadableEvtMsg(header, body)
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
bus.outGW.send(msgEvent)
}
def broadcastPresentationConversionCompletedEvtMsg(
bus: MessageBus,
meetingId: String,
podId: String, userId: String, messageKey: String,
code: String, presentation: PresentationVO
): Unit = {
val routing = Routing.addMsgToClientRouting(
MessageTypes.BROADCAST_TO_MEETING,
meetingId, userId
)
val envelope = BbbCoreEnvelope(PresentationConversionCompletedEvtMsg.NAME, routing)
val header = BbbClientMsgHeader(PresentationConversionCompletedEvtMsg.NAME, meetingId, userId)
val body = PresentationConversionCompletedEvtMsgBody(podId, messageKey, code, presentation)
val event = PresentationConversionCompletedEvtMsg(header, body)
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
bus.outGW.send(msgEvent)
}
}

View File

@ -14,26 +14,14 @@ trait SetPresentationDownloadablePubMsgHdlr extends RightsManagementTrait {
liveMeeting: LiveMeeting, bus: MessageBus liveMeeting: LiveMeeting, bus: MessageBus
): MeetingState2x = { ): MeetingState2x = {
val meetingId = liveMeeting.props.meetingProp.intId
if (filterPresentationMessage(liveMeeting.users2x, msg.header.userId) && if (filterPresentationMessage(liveMeeting.users2x, msg.header.userId) &&
permissionFailed(PermissionCheck.GUEST_LEVEL, PermissionCheck.PRESENTER_LEVEL, liveMeeting.users2x, msg.header.userId)) { permissionFailed(PermissionCheck.GUEST_LEVEL, PermissionCheck.PRESENTER_LEVEL, liveMeeting.users2x, msg.header.userId)) {
val meetingId = liveMeeting.props.meetingProp.intId
val reason = "No permission to remove presentation from meeting." val reason = "No permission to remove presentation from meeting."
PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, bus.outGW, liveMeeting) PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, bus.outGW, liveMeeting)
state state
} else { } else {
def broadcastSetPresentationDownloadableEvtMsg(podId: String, userId: String, presentationId: String, downloadable: Boolean): Unit = {
val routing = Routing.addMsgToClientRouting(
MessageTypes.BROADCAST_TO_MEETING,
liveMeeting.props.meetingProp.intId, userId
)
val envelope = BbbCoreEnvelope(SetPresentationDownloadableEvtMsg.NAME, routing)
val header = BbbClientMsgHeader(SetPresentationDownloadableEvtMsg.NAME, liveMeeting.props.meetingProp.intId, userId)
val body = SetPresentationDownloadableEvtMsgBody(podId, presentationId, downloadable)
val event = SetPresentationDownloadableEvtMsg(header, body)
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
bus.outGW.send(msgEvent)
}
val podId = msg.body.podId val podId = msg.body.podId
val presentationId = msg.body.presentationId val presentationId = msg.body.presentationId
@ -41,8 +29,10 @@ trait SetPresentationDownloadablePubMsgHdlr extends RightsManagementTrait {
val newState = for { val newState = for {
pod <- PresentationPodsApp.getPresentationPod(state, podId) pod <- PresentationPodsApp.getPresentationPod(state, podId)
pres <- pod.getPresentation(presentationId)
} yield { } yield {
broadcastSetPresentationDownloadableEvtMsg(pod.id, msg.header.userId, presentationId, downloadable) PresentationSender.broadcastSetPresentationDownloadableEvtMsg(bus, meetingId, pod.id,
msg.header.userId, presentationId, downloadable, pres.name)
val pods = state.presentationPodManager.setPresentationDownloadableInPod(pod.id, presentationId, downloadable) val pods = state.presentationPodManager.setPresentationDownloadableInPod(pod.id, presentationId, downloadable)
state.update(pods) state.update(pods)

View File

@ -14,7 +14,7 @@ trait ChangeLockSettingsInMeetingCmdMsgHdlr extends RightsManagementTrait {
def handleSetLockSettings(msg: ChangeLockSettingsInMeetingCmdMsg): Unit = { def handleSetLockSettings(msg: ChangeLockSettingsInMeetingCmdMsg): Unit = {
if (permissionFailed(PermissionCheck.MOD_LEVEL, PermissionCheck.PRESENTER_LEVEL, liveMeeting.users2x, msg.header.userId)) { if (permissionFailed(PermissionCheck.MOD_LEVEL, PermissionCheck.VIEWER_LEVEL, liveMeeting.users2x, msg.header.userId)) {
val meetingId = liveMeeting.props.meetingProp.intId val meetingId = liveMeeting.props.meetingProp.intId
val reason = "No permission to change lock settings" val reason = "No permission to change lock settings"
PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, outGW, liveMeeting) PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, outGW, liveMeeting)

View File

@ -1,7 +1,7 @@
package org.bigbluebutton.core.apps.users package org.bigbluebutton.core.apps.users
import org.bigbluebutton.common2.msgs._ import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.models.{ Roles, Users2x } import org.bigbluebutton.core.models.{ RegisteredUsers, Roles, Users2x }
import org.bigbluebutton.core.running.{ LiveMeeting, OutMsgRouter } import org.bigbluebutton.core.running.{ LiveMeeting, OutMsgRouter }
import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait } import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait }
@ -20,8 +20,17 @@ trait ChangeUserRoleCmdMsgHdlr extends RightsManagementTrait {
for { for {
uvo <- Users2x.findWithIntId(liveMeeting.users2x, msg.body.userId) uvo <- Users2x.findWithIntId(liveMeeting.users2x, msg.body.userId)
} yield { } yield {
if (msg.body.role == Roles.MODERATOR_ROLE && uvo.authed) {
// Promote only authenticated users. val userRole = if (uvo.role == Roles.MODERATOR_ROLE) "MODERATOR" else "VIEWER"
for {
// Update guest from waiting list
u <- RegisteredUsers.findWithUserId(uvo.intId, liveMeeting.registeredUsers)
} yield {
RegisteredUsers.updateUserRole(liveMeeting.registeredUsers, u, userRole)
}
if (msg.body.role == Roles.MODERATOR_ROLE && !uvo.guest) {
// Promote non-guest users.
Users2x.changeRole(liveMeeting.users2x, uvo, msg.body.role) Users2x.changeRole(liveMeeting.users2x, uvo, msg.body.role)
val event = buildUserRoleChangedEvtMsg(liveMeeting.props.meetingProp.intId, msg.body.userId, val event = buildUserRoleChangedEvtMsg(liveMeeting.props.meetingProp.intId, msg.body.userId,
msg.body.changedBy, "MODERATOR") msg.body.changedBy, "MODERATOR")

View File

@ -12,10 +12,10 @@ trait GetRecordingStatusReqMsgHdlr {
def handleGetRecordingStatusReqMsg(msg: GetRecordingStatusReqMsg) { def handleGetRecordingStatusReqMsg(msg: GetRecordingStatusReqMsg) {
def buildGetRecordingStatusRespMsg(meetingId: String, userId: String, recording: Boolean): BbbCommonEnvCoreMsg = { def buildGetRecordingStatusRespMsg(meetingId: String, userId: String, recorded: Boolean, recording: Boolean): BbbCommonEnvCoreMsg = {
val routing = Routing.addMsgToClientRouting(MessageTypes.DIRECT, meetingId, userId) val routing = Routing.addMsgToClientRouting(MessageTypes.DIRECT, meetingId, userId)
val envelope = BbbCoreEnvelope(GetRecordingStatusRespMsg.NAME, routing) val envelope = BbbCoreEnvelope(GetRecordingStatusRespMsg.NAME, routing)
val body = GetRecordingStatusRespMsgBody(recording, userId) val body = GetRecordingStatusRespMsgBody(recorded, recording, userId)
val header = BbbClientMsgHeader(GetRecordingStatusRespMsg.NAME, meetingId, userId) val header = BbbClientMsgHeader(GetRecordingStatusRespMsg.NAME, meetingId, userId)
val event = GetRecordingStatusRespMsg(header, body) val event = GetRecordingStatusRespMsg(header, body)
@ -23,7 +23,7 @@ trait GetRecordingStatusReqMsgHdlr {
} }
val event = buildGetRecordingStatusRespMsg(liveMeeting.props.meetingProp.intId, msg.body.requestedBy, val event = buildGetRecordingStatusRespMsg(liveMeeting.props.meetingProp.intId, msg.body.requestedBy,
MeetingStatus2x.isRecording(liveMeeting.status)) liveMeeting.props.recordProp.record, MeetingStatus2x.isRecording(liveMeeting.status))
outGW.send(event) outGW.send(event)
} }
} }

View File

@ -34,9 +34,9 @@ trait MuteUserCmdMsgHdlr extends RightsManagementTrait {
requester <- Users2x.findWithIntId(liveMeeting.users2x, msg.header.userId) requester <- Users2x.findWithIntId(liveMeeting.users2x, msg.header.userId)
u <- VoiceUsers.findWithIntId(liveMeeting.voiceUsers, msg.body.userId) u <- VoiceUsers.findWithIntId(liveMeeting.voiceUsers, msg.body.userId)
} yield { } yield {
if (requester.role != Roles.MODERATOR_ROLE && permissions.disableMic && if (requester.role != Roles.MODERATOR_ROLE && permissions.disableMic && u.muted &&
msg.body.userId == msg.header.userId) { msg.body.userId == msg.header.userId) {
// muting/unmuting self while not moderator and mic disabled. Do not allow. // unmuting self while not moderator and mic disabled. Do not allow.
} else { } else {
if (u.muted != msg.body.mute) { if (u.muted != msg.body.mute) {
log.info("Send mute user request. meetingId=" + meetingId + " userId=" + u.intId + " user=" + u) log.info("Send mute user request. meetingId=" + meetingId + " userId=" + u.intId + " user=" + u)

View File

@ -0,0 +1,46 @@
package org.bigbluebutton.core.apps.users
import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.domain.MeetingState2x
import org.bigbluebutton.core.running.{ LiveMeeting, OutMsgRouter }
import org.bigbluebutton.core2.MeetingStatus2x
import org.bigbluebutton.core.util.TimeUtil
import org.bigbluebutton.core2.message.senders.MsgBuilder
trait RecordAndClearPreviousMarkersCmdMsgHdlr {
this: UsersApp =>
val liveMeeting: LiveMeeting
val outGW: OutMsgRouter
def handleRecordAndClearPreviousMarkersCmdMsg(msg: RecordAndClearPreviousMarkersCmdMsg, state: MeetingState2x): MeetingState2x = {
log.info("Set a new recording marker and clear previous ones. meetingId=" + liveMeeting.props.meetingProp.intId + " recording=" + msg.body.recording)
def buildRecordingStatusChangedEvtMsg(meetingId: String, userId: String, recording: Boolean): BbbCommonEnvCoreMsg = {
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, userId)
val envelope = BbbCoreEnvelope(RecordingStatusChangedEvtMsg.NAME, routing)
val body = RecordingStatusChangedEvtMsgBody(recording, userId)
val header = BbbClientMsgHeader(RecordingStatusChangedEvtMsg.NAME, meetingId, userId)
val event = RecordingStatusChangedEvtMsg(header, body)
BbbCommonEnvCoreMsg(envelope, event)
}
// Do not allow stop recording and clear previous markers
if (liveMeeting.props.recordProp.allowStartStopRecording &&
MeetingStatus2x.isRecording(liveMeeting.status) != msg.body.recording) {
MeetingStatus2x.recordingStarted(liveMeeting.status)
val tracker = state.recordingTracker.resetTimer(TimeUtil.timeNowInMs())
val event = buildRecordingStatusChangedEvtMsg(liveMeeting.props.meetingProp.intId, msg.body.setBy, msg.body.recording)
outGW.send(event)
outGW.send(MsgBuilder.buildRecordStatusResetSysMsg(liveMeeting.props.meetingProp.intId, msg.body.recording, msg.body.setBy))
state.update(tracker)
} else {
state
}
}
}

View File

@ -25,8 +25,6 @@ trait RegisterUserReqMsgHdlr {
val guestPolicy = liveMeeting.guestsWaiting.getGuestPolicy().policy val guestPolicy = liveMeeting.guestsWaiting.getGuestPolicy().policy
val guestStatus = msg.body.guestStatus val guestStatus = msg.body.guestStatus
println("****** GUEST POLICY = " + guestPolicy + " guestStatus = " + guestStatus)
val regUser = RegisteredUsers.create(msg.body.intUserId, msg.body.extUserId, val regUser = RegisteredUsers.create(msg.body.intUserId, msg.body.extUserId,
msg.body.name, msg.body.role, msg.body.authToken, msg.body.name, msg.body.role, msg.body.authToken,
msg.body.avatarURL, msg.body.guest, msg.body.authed, guestStatus) msg.body.avatarURL, msg.body.guest, msg.body.authed, guestStatus)

View File

@ -0,0 +1,43 @@
package org.bigbluebutton.core.apps.users
import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.api.SendRecordingTimerInternalMsg
import org.bigbluebutton.core.domain.MeetingState2x
import org.bigbluebutton.core.running.{ LiveMeeting, OutMsgRouter }
import org.bigbluebutton.core.util.TimeUtil
import org.bigbluebutton.core2.MeetingStatus2x
trait SendRecordingTimerInternalMsgHdlr {
this: UsersApp =>
val liveMeeting: LiveMeeting
val outGW: OutMsgRouter
def handleSendRecordingTimerInternalMsg(msg: SendRecordingTimerInternalMsg, state: MeetingState2x): MeetingState2x = {
def buildUpdateRecordingTimerEvtMsg(meetingId: String, recordingTime: Long): BbbCommonEnvCoreMsg = {
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, "not-used")
val envelope = BbbCoreEnvelope(UpdateRecordingTimerEvtMsg.NAME, routing)
val body = UpdateRecordingTimerEvtMsgBody(recordingTime)
val header = BbbClientMsgHeader(UpdateRecordingTimerEvtMsg.NAME, meetingId, "not-used")
val event = UpdateRecordingTimerEvtMsg(header, body)
BbbCommonEnvCoreMsg(envelope, event)
}
var newDuration = 0L
if (MeetingStatus2x.isRecording(liveMeeting.status)) {
newDuration = TimeUtil.timeNowInMs()
val tracker = state.recordingTracker.udpateCurrentDuration(newDuration)
val recordingTime = TimeUtil.millisToSeconds(tracker.recordingDuration())
val event = buildUpdateRecordingTimerEvtMsg(liveMeeting.props.meetingProp.intId, recordingTime)
outGW.send(event)
state.update(tracker)
} else {
state
}
}
}

View File

@ -1,45 +1,64 @@
package org.bigbluebutton.core.apps.users package org.bigbluebutton.core.apps.users
import org.bigbluebutton.common2.msgs._ import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.running.{ LiveMeeting, OutMsgRouter } import org.bigbluebutton.core.domain.MeetingState2x
import org.bigbluebutton.core2.MeetingStatus2x import org.bigbluebutton.core.running.{ LiveMeeting, OutMsgRouter }
import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait } import org.bigbluebutton.core2.MeetingStatus2x
import org.bigbluebutton.core.util.TimeUtil
trait SetRecordingStatusCmdMsgHdlr extends RightsManagementTrait { import org.bigbluebutton.core.bus.BigBlueButtonEvent
this: UsersApp => import org.bigbluebutton.core.api.SendRecordingTimerInternalMsg
import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait }
val liveMeeting: LiveMeeting
val outGW: OutMsgRouter trait SetRecordingStatusCmdMsgHdlr extends RightsManagementTrait {
this: UsersApp =>
def handleSetRecordingStatusCmdMsg(msg: SetRecordingStatusCmdMsg) {
log.info("Change recording status. meetingId=" + liveMeeting.props.meetingProp.intId + " recording=" + msg.body.recording) val liveMeeting: LiveMeeting
val outGW: OutMsgRouter
if (permissionFailed(PermissionCheck.MOD_LEVEL, PermissionCheck.VIEWER_LEVEL, liveMeeting.users2x, msg.header.userId)) {
val meetingId = liveMeeting.props.meetingProp.intId def handleSetRecordingStatusCmdMsg(msg: SetRecordingStatusCmdMsg, state: MeetingState2x): MeetingState2x = {
val reason = "No permission to clear chat in meeting." log.info("Change recording status. meetingId=" + liveMeeting.props.meetingProp.intId + " recording=" + msg.body.recording)
PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, outGW, liveMeeting)
} else { def buildRecordingStatusChangedEvtMsg(meetingId: String, userId: String, recording: Boolean): BbbCommonEnvCoreMsg = {
if (liveMeeting.props.recordProp.allowStartStopRecording && val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, userId)
MeetingStatus2x.isRecording(liveMeeting.status) != msg.body.recording) { val envelope = BbbCoreEnvelope(RecordingStatusChangedEvtMsg.NAME, routing)
if (msg.body.recording) { val body = RecordingStatusChangedEvtMsgBody(recording, userId)
MeetingStatus2x.recordingStarted(liveMeeting.status) val header = BbbClientMsgHeader(RecordingStatusChangedEvtMsg.NAME, meetingId, userId)
} else { val event = RecordingStatusChangedEvtMsg(header, body)
MeetingStatus2x.recordingStopped(liveMeeting.status)
} BbbCommonEnvCoreMsg(envelope, event)
}
val event = buildRecordingStatusChangedEvtMsg(liveMeeting.props.meetingProp.intId, msg.body.setBy, msg.body.recording)
outGW.send(event) if (permissionFailed(PermissionCheck.MOD_LEVEL, PermissionCheck.VIEWER_LEVEL, liveMeeting.users2x, msg.header.userId)) {
} val meetingId = liveMeeting.props.meetingProp.intId
} val reason = "No permission to clear chat in meeting."
PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, outGW, liveMeeting)
def buildRecordingStatusChangedEvtMsg(meetingId: String, userId: String, recording: Boolean): BbbCommonEnvCoreMsg = { state
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, userId) } else {
val envelope = BbbCoreEnvelope(RecordingStatusChangedEvtMsg.NAME, routing) if (liveMeeting.props.recordProp.allowStartStopRecording &&
val body = RecordingStatusChangedEvtMsgBody(recording, userId) MeetingStatus2x.isRecording(liveMeeting.status) != msg.body.recording) {
val header = BbbClientMsgHeader(RecordingStatusChangedEvtMsg.NAME, meetingId, userId) if (msg.body.recording) {
val event = RecordingStatusChangedEvtMsg(header, body) MeetingStatus2x.recordingStarted(liveMeeting.status)
} else {
BbbCommonEnvCoreMsg(envelope, event) MeetingStatus2x.recordingStopped(liveMeeting.status)
} }
}
} val event = buildRecordingStatusChangedEvtMsg(liveMeeting.props.meetingProp.intId, msg.body.setBy, msg.body.recording)
outGW.send(event)
var newState = state
if (MeetingStatus2x.isRecording(liveMeeting.status)) {
val tracker = state.recordingTracker.startTimer(TimeUtil.timeNowInMs())
newState = state.update(tracker)
} else {
val tracker = state.recordingTracker.pauseTimer(TimeUtil.timeNowInMs())
newState = state.update(tracker)
}
eventBus.publish(BigBlueButtonEvent(liveMeeting.props.meetingProp.intId, SendRecordingTimerInternalMsg(liveMeeting.props.meetingProp.intId)))
newState
} else {
state
}
}
}
}

View File

@ -19,9 +19,20 @@ trait SyncGetUsersMeetingRespMsgHdlr {
val users = Users2x.findAll(liveMeeting.users2x) val users = Users2x.findAll(liveMeeting.users2x)
val webUsers = users.map { u => val webUsers = users.map { u =>
WebUser(intId = u.intId, extId = u.extId, name = u.name, role = u.role, WebUser(
guest = u.guest, authed = u.authed, guestStatus = u.guestStatus, emoji = u.emoji, intId = u.intId,
locked = u.locked, presenter = u.presenter, avatar = u.avatar) extId = u.extId,
name = u.name,
role = u.role,
guest = u.guest,
authed = u.authed,
guestStatus = u.guestStatus,
emoji = u.emoji,
locked = u.locked,
presenter = u.presenter,
avatar = u.avatar,
clientType = u.clientType
)
} }
val body = SyncGetUsersMeetingRespMsgBody(webUsers) val body = SyncGetUsersMeetingRespMsgBody(webUsers)

View File

@ -0,0 +1,17 @@
package org.bigbluebutton.core.apps.users
import org.bigbluebutton.common2.msgs.UserActivitySignCmdMsg
import org.bigbluebutton.core.models.Users2x
import org.bigbluebutton.core.running.MeetingActor
trait UserActivitySignCmdMsgHdlr {
this: MeetingActor =>
def handleUserActivitySignCmdMsg(msg: UserActivitySignCmdMsg): Unit = {
for {
user <- Users2x.findWithIntId(liveMeeting.users2x, msg.body.userId)
} yield {
Users2x.updateLastUserActivity(liveMeeting.users2x, user)
}
}
}

View File

@ -18,15 +18,6 @@ trait UserBroadcastCamStopMsgHdlr {
} }
} }
def handleUserBroadcastCamStopMsg(userId: String): Unit = {
for {
uvo <- Webcams.findWebcamsForUser(liveMeeting.webcams, userId)
_ <- Webcams.removeWebcamBroadcastStream(liveMeeting.webcams, uvo.streamId)
} yield {
broadcastUserBroadcastCamStoppedEvtMsg(uvo.streamId, userId)
}
}
def broadcastUserBroadcastCamStoppedEvtMsg(streamId: String, userId: String): Unit = { def broadcastUserBroadcastCamStoppedEvtMsg(streamId: String, userId: String): Unit = {
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, props.meetingProp.intId, userId) val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, props.meetingProp.intId, userId)
val envelope = BbbCoreEnvelope(UserBroadcastCamStoppedEvtMsg.NAME, routing) val envelope = BbbCoreEnvelope(UserBroadcastCamStoppedEvtMsg.NAME, routing)

View File

@ -14,23 +14,11 @@ trait UserJoinMeetingAfterReconnectReqMsgHdlr extends HandlerHelpers with Breako
def handleUserJoinMeetingAfterReconnectReqMsg(msg: UserJoinMeetingAfterReconnectReqMsg, state: MeetingState2x): MeetingState2x = { def handleUserJoinMeetingAfterReconnectReqMsg(msg: UserJoinMeetingAfterReconnectReqMsg, state: MeetingState2x): MeetingState2x = {
val newState = userJoinMeeting(outGW, msg.body.authToken, liveMeeting, state) val newState = userJoinMeeting(outGW, msg.body.authToken, msg.body.clientType, liveMeeting, state)
if (liveMeeting.props.meetingProp.isBreakout) { if (liveMeeting.props.meetingProp.isBreakout) {
updateParentMeetingWithUsers() updateParentMeetingWithUsers()
} }
/*
* *
* // recover voice user
* for {
* vu <- VoiceUsers.recoverVoiceUser(liveMeeting.voiceUsers, msg.body.userId)
* } yield {
* handleUserJoinedVoiceConfEvtMsg(liveMeeting.props.voiceProp.voiceConf, vu.intId, vu.voiceUserId, vu.callingWith, vu.callerName, vu.callerNum, vu.muted, vu.talking)
* }
*
* newState
*/
newState newState
} }

View File

@ -13,7 +13,7 @@ trait UserJoinMeetingReqMsgHdlr extends HandlerHelpers with BreakoutHdlrHelpers
val outGW: OutMsgRouter val outGW: OutMsgRouter
def handleUserJoinMeetingReqMsg(msg: UserJoinMeetingReqMsg, state: MeetingState2x): MeetingState2x = { def handleUserJoinMeetingReqMsg(msg: UserJoinMeetingReqMsg, state: MeetingState2x): MeetingState2x = {
val newState = userJoinMeeting(outGW, msg.body.authToken, liveMeeting, state) val newState = userJoinMeeting(outGW, msg.body.authToken, msg.body.clientType, liveMeeting, state)
if (liveMeeting.props.meetingProp.isBreakout) { if (liveMeeting.props.meetingProp.isBreakout) {
updateParentMeetingWithUsers() updateParentMeetingWithUsers()

View File

@ -1,11 +1,14 @@
package org.bigbluebutton.core.apps.users package org.bigbluebutton.core.apps.users
import org.bigbluebutton.common2.domain.MeetingStatus
import org.bigbluebutton.common2.msgs.UserLeaveReqMsg
import org.bigbluebutton.common2.msgs._ import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.apps.presentationpod.PresentationPodsApp import org.bigbluebutton.core.apps.presentationpod.PresentationPodsApp
import org.bigbluebutton.core.domain.MeetingState2x import org.bigbluebutton.core.domain.MeetingState2x
import org.bigbluebutton.core.models.Users2x import org.bigbluebutton.core.models.Users2x
import org.bigbluebutton.core.running.{ MeetingActor, OutMsgRouter } import org.bigbluebutton.core.running.{ MeetingActor, OutMsgRouter }
import org.bigbluebutton.core.util.TimeUtil import org.bigbluebutton.core.util.TimeUtil
import org.bigbluebutton.core2.MeetingStatus2x
import org.bigbluebutton.core2.message.senders.MsgBuilder import org.bigbluebutton.core2.message.senders.MsgBuilder
trait UserLeaveReqMsgHdlr { trait UserLeaveReqMsgHdlr {
@ -35,11 +38,13 @@ trait UserLeaveReqMsgHdlr {
} yield { } yield {
log.info("User left meeting. meetingId=" + props.meetingProp.intId + " userId=" + u.intId + " user=" + u) log.info("User left meeting. meetingId=" + props.meetingProp.intId + " userId=" + u.intId + " user=" + u)
// stop the webcams of a user leaving val authedUsers = Users2x.findAllAuthedUsers(liveMeeting.users2x)
handleUserBroadcastCamStopMsg(msg.body.userId) if (u.authed && authedUsers.isEmpty) {
MeetingStatus2x.setLastAuthedUserLeftOn(liveMeeting.status)
}
captionApp2x.handleUserLeavingMsg(msg.body.userId, liveMeeting, msgBus) captionApp2x.handleUserLeavingMsg(msg.body.userId, liveMeeting, msgBus)
stopAutoStartedRecording() stopRecordingIfAutoStart2x(outGW, liveMeeting, state)
// send a user left event for the clients to update // send a user left event for the clients to update
val userLeftMeetingEvent = MsgBuilder.buildUserLeftMeetingEvtMsg(liveMeeting.props.meetingProp.intId, u.intId) val userLeftMeetingEvent = MsgBuilder.buildUserLeftMeetingEvtMsg(liveMeeting.props.meetingProp.intId, u.intId)

View File

@ -129,6 +129,8 @@ class UsersApp(
with LogoutAndEndMeetingCmdMsgHdlr with LogoutAndEndMeetingCmdMsgHdlr
with MeetingActivityResponseCmdMsgHdlr with MeetingActivityResponseCmdMsgHdlr
with SetRecordingStatusCmdMsgHdlr with SetRecordingStatusCmdMsgHdlr
with RecordAndClearPreviousMarkersCmdMsgHdlr
with SendRecordingTimerInternalMsgHdlr
with UpdateWebcamsOnlyForModeratorCmdMsgHdlr with UpdateWebcamsOnlyForModeratorCmdMsgHdlr
with GetRecordingStatusReqMsgHdlr with GetRecordingStatusReqMsgHdlr
with GetWebcamsOnlyForModeratorReqMsgHdlr with GetWebcamsOnlyForModeratorReqMsgHdlr

View File

@ -66,7 +66,7 @@ trait ValidateAuthTokenReqMsgHdlr extends HandlerHelpers {
val webUsers = users.map { u => val webUsers = users.map { u =>
WebUser(intId = u.intId, extId = u.extId, name = u.name, role = u.role, WebUser(intId = u.intId, extId = u.extId, name = u.name, role = u.role,
guest = u.guest, authed = u.authed, guestStatus = u.guestStatus, emoji = u.emoji, guest = u.guest, authed = u.authed, guestStatus = u.guestStatus, emoji = u.emoji,
locked = u.locked, presenter = u.presenter, avatar = u.avatar) locked = u.locked, presenter = u.presenter, avatar = u.avatar, clientType = u.clientType)
} }
val event = MsgBuilder.buildGetUsersMeetingRespMsg(meetingId, requesterId, webUsers) val event = MsgBuilder.buildGetUsersMeetingRespMsg(meetingId, requesterId, webUsers)

View File

@ -0,0 +1,34 @@
package org.bigbluebutton.core.apps.voice
import org.bigbluebutton.core.running.{ BaseMeetingActor, LiveMeeting }
import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.bus.MessageBus
import org.bigbluebutton.core.models.VoiceUsers
import org.bigbluebutton.core.domain.MeetingState2x
trait SyncGetVoiceUsersMsgHdlr {
this: BaseMeetingActor =>
def handleSyncGetVoiceUsersMsg(state: MeetingState2x, liveMeeting: LiveMeeting, bus: MessageBus): MeetingState2x = {
def buildSyncGetVoiceUsersRespMsg(): BbbCommonEnvCoreMsg = {
val voiceUsers = VoiceUsers.findAll(liveMeeting.voiceUsers).map { u =>
VoiceConfUser(intId = u.intId, voiceUserId = u.voiceUserId, callingWith = u.callingWith, callerName = u.callerName,
callerNum = u.callerNum, muted = u.muted, talking = u.talking, listenOnly = u.listenOnly)
}
val routing = Routing.addMsgToClientRouting(MessageTypes.DIRECT, liveMeeting.props.meetingProp.intId, "nodeJSapp")
val envelope = BbbCoreEnvelope(SyncGetVoiceUsersRespMsg.NAME, routing)
val header = BbbClientMsgHeader(SyncGetVoiceUsersRespMsg.NAME, liveMeeting.props.meetingProp.intId, "nodeJSapp")
val body = SyncGetVoiceUsersRespMsgBody(voiceUsers)
val event = SyncGetVoiceUsersRespMsg(header, body)
BbbCommonEnvCoreMsg(envelope, event)
}
val respMsg = buildSyncGetVoiceUsersRespMsg()
bus.outGW.send(respMsg)
state
}
}

View File

@ -9,7 +9,8 @@ trait VoiceApp2x extends UserJoinedVoiceConfEvtMsgHdlr
with UserMutedInVoiceConfEvtMsgHdlr with UserMutedInVoiceConfEvtMsgHdlr
with UserTalkingInVoiceConfEvtMsgHdlr with UserTalkingInVoiceConfEvtMsgHdlr
with RecordingStartedVoiceConfEvtMsgHdlr with RecordingStartedVoiceConfEvtMsgHdlr
with VoiceConfRunningEvtMsgHdlr { with VoiceConfRunningEvtMsgHdlr
with SyncGetVoiceUsersMsgHdlr {
this: MeetingActor => this: MeetingActor =>
} }

View File

@ -6,6 +6,7 @@ case class BreakoutRoom2x(
name: String, name: String,
parentId: String, parentId: String,
sequence: Int, sequence: Int,
freeJoin: Boolean,
voiceConf: String, voiceConf: String,
assignedUsers: Vector[String], assignedUsers: Vector[String],
users: Vector[BreakoutUser], users: Vector[BreakoutUser],

View File

@ -20,7 +20,9 @@ case class MeetingInactivityTracker(
} }
def hasRecentActivity(nowInMs: Long): Boolean = { def hasRecentActivity(nowInMs: Long): Boolean = {
nowInMs - lastActivityTimestampInMs < maxInactivityTimeoutInMs - warningBeforeMaxInMs val left = nowInMs - lastActivityTimestampInMs
val right = maxInactivityTimeoutInMs - warningBeforeMaxInMs
left < right
} }
def isMeetingInactive(nowInMs: Long): Boolean = { def isMeetingInactive(nowInMs: Long): Boolean = {
@ -35,10 +37,14 @@ case class MeetingInactivityTracker(
case class MeetingExpiryTracker( case class MeetingExpiryTracker(
startedOnInMs: Long, startedOnInMs: Long,
userHasJoined: Boolean, userHasJoined: Boolean,
isBreakout: Boolean,
lastUserLeftOnInMs: Option[Long], lastUserLeftOnInMs: Option[Long],
durationInMs: Long, durationInMs: Long,
meetingExpireIfNoUserJoinedInMs: Long, meetingExpireIfNoUserJoinedInMs: Long,
meetingExpireWhenLastUserLeftInMs: Long meetingExpireWhenLastUserLeftInMs: Long,
userInactivityInspectTimerInMs: Long,
userInactivityThresholdInMs: Long,
userActivitySignResponseDelayInMs: Long
) { ) {
def setUserHasJoined(): MeetingExpiryTracker = { def setUserHasJoined(): MeetingExpiryTracker = {
if (!userHasJoined) { if (!userHasJoined) {
@ -63,11 +69,11 @@ case class MeetingExpiryTracker(
} }
def hasMeetingExpired(timestampInMs: Long): (Boolean, Option[String]) = { def hasMeetingExpired(timestampInMs: Long): (Boolean, Option[String]) = {
if (hasMeetingExpiredNeverBeenJoined(timestampInMs)) { if (hasMeetingExpiredNeverBeenJoined(timestampInMs) && !isBreakout) {
(true, Some(MeetingEndReason.ENDED_WHEN_NOT_JOINED)) (true, Some(MeetingEndReason.ENDED_WHEN_NOT_JOINED))
} else if (meetingOverDuration(timestampInMs)) { } else if (meetingOverDuration(timestampInMs)) {
(true, Some(MeetingEndReason.ENDED_AFTER_EXCEEDING_DURATION)) (true, Some(MeetingEndReason.ENDED_AFTER_EXCEEDING_DURATION))
} else if (hasMeetingExpiredAfterLastUserLeft(timestampInMs)) { } else if (hasMeetingExpiredAfterLastUserLeft(timestampInMs) && !isBreakout) {
(true, Some(MeetingEndReason.ENDED_WHEN_LAST_USER_LEFT)) (true, Some(MeetingEndReason.ENDED_WHEN_LAST_USER_LEFT))
} else { } else {
(false, None) (false, None)
@ -91,3 +97,31 @@ case class MeetingExpiryTracker(
} }
} }
case class MeetingRecordingTracker(
startedOnInMs: Long,
previousDurationInMs: Long,
currentDurationInMs: Long
) {
def startTimer(nowInMs: Long): MeetingRecordingTracker = {
copy(startedOnInMs = nowInMs)
}
def pauseTimer(nowInMs: Long): MeetingRecordingTracker = {
copy(currentDurationInMs = 0L, previousDurationInMs = previousDurationInMs + nowInMs - startedOnInMs, startedOnInMs = 0L)
}
def resetTimer(nowInMs: Long): MeetingRecordingTracker = {
copy(startedOnInMs = nowInMs, previousDurationInMs = 0L, currentDurationInMs = 0L)
}
def udpateCurrentDuration(nowInMs: Long): MeetingRecordingTracker = {
copy(currentDurationInMs = nowInMs - startedOnInMs)
}
def recordingDuration(): Long = {
currentDurationInMs + previousDurationInMs
}
}

View File

@ -13,7 +13,8 @@ case class MeetingState2x(
presentationPodManager: PresentationPodManager, presentationPodManager: PresentationPodManager,
breakout: Option[BreakoutModel], breakout: Option[BreakoutModel],
inactivityTracker: MeetingInactivityTracker, inactivityTracker: MeetingInactivityTracker,
expiryTracker: MeetingExpiryTracker expiryTracker: MeetingExpiryTracker,
recordingTracker: MeetingRecordingTracker
) { ) {
def update(groupChats: GroupChats): MeetingState2x = copy(groupChats = groupChats) def update(groupChats: GroupChats): MeetingState2x = copy(groupChats = groupChats)
@ -21,14 +22,16 @@ case class MeetingState2x(
def update(breakout: Option[BreakoutModel]): MeetingState2x = copy(breakout = breakout) def update(breakout: Option[BreakoutModel]): MeetingState2x = copy(breakout = breakout)
def update(expiry: MeetingExpiryTracker): MeetingState2x = copy(expiryTracker = expiry) def update(expiry: MeetingExpiryTracker): MeetingState2x = copy(expiryTracker = expiry)
def update(inactivityTracker: MeetingInactivityTracker): MeetingState2x = copy(inactivityTracker = inactivityTracker) def update(inactivityTracker: MeetingInactivityTracker): MeetingState2x = copy(inactivityTracker = inactivityTracker)
def update(recordingTracker: MeetingRecordingTracker): MeetingState2x = copy(recordingTracker = recordingTracker)
} }
object MeetingEndReason { object MeetingEndReason {
val ENDED_FROM_API = "ENDED_FROM_API" val ENDED_FROM_API = "ENDED_FROM_API"
val ENDED_DUE_TO_INACTIVITY = "ENDED_DUE_TO_ACTIVITY" val ENDED_DUE_TO_INACTIVITY = "ENDED_DUE_TO_INACTIVITY"
val ENDED_WHEN_NOT_JOINED = "ENDED_WHEN_NOT_JOINED" val ENDED_WHEN_NOT_JOINED = "ENDED_WHEN_NOT_JOINED"
val ENDED_WHEN_LAST_USER_LEFT = "ENDED_WHEN_LAST_USER_LEFT" val ENDED_WHEN_LAST_USER_LEFT = "ENDED_WHEN_LAST_USER_LEFT"
val ENDED_AFTER_USER_LOGGED_OUT = "ENDED_AFTER_USER_LOGGED_OUT" val ENDED_AFTER_USER_LOGGED_OUT = "ENDED_AFTER_USER_LOGGED_OUT"
val ENDED_AFTER_EXCEEDING_DURATION = "ENDED_AFTER_EXCEEDING_DURATION" val ENDED_AFTER_EXCEEDING_DURATION = "ENDED_AFTER_EXCEEDING_DURATION"
val ENDED_BY_PARENT = "ENDED_BY_PARENT" val ENDED_BY_PARENT = "ENDED_BY_PARENT"
val ENDED_DUE_TO_NO_AUTHED_USER = "ENDED_DUE_TO_NO_AUTHED_USER"
} }

View File

@ -9,9 +9,9 @@ object BreakoutRooms {
def breakoutRoomsdurationInMinutes(status: BreakoutRooms) = status.breakoutRoomsdurationInMinutes def breakoutRoomsdurationInMinutes(status: BreakoutRooms) = status.breakoutRoomsdurationInMinutes
def breakoutRoomsdurationInMinutes(status: BreakoutRooms, duration: Int) = status.breakoutRoomsdurationInMinutes = duration def breakoutRoomsdurationInMinutes(status: BreakoutRooms, duration: Int) = status.breakoutRoomsdurationInMinutes = duration
def newBreakoutRoom(parentRoomId: String, id: String, externalMeetingId: String, name: String, sequence: Integer, voiceConfId: String, def newBreakoutRoom(parentRoomId: String, id: String, externalMeetingId: String, name: String, sequence: Integer, freeJoin: Boolean,
assignedUsers: Vector[String], breakoutRooms: BreakoutRooms): Option[BreakoutRoomVO] = { voiceConfId: String, assignedUsers: Vector[String], breakoutRooms: BreakoutRooms): Option[BreakoutRoomVO] = {
val brvo = new BreakoutRoomVO(id, externalMeetingId, name, parentRoomId, sequence, voiceConfId, assignedUsers, Vector()) val brvo = new BreakoutRoomVO(id, externalMeetingId, name, parentRoomId, sequence, freeJoin, voiceConfId, assignedUsers, Vector())
breakoutRooms.add(brvo) breakoutRooms.add(brvo)
Some(brvo) Some(brvo)
} }

View File

@ -20,6 +20,7 @@ case class GroupChats(chats: collection.immutable.Map[String, GroupChat]) {
def findAllPublicChats(): Vector[GroupChat] = chats.values.toVector filter (c => c.access == GroupChatAccess.PUBLIC) def findAllPublicChats(): Vector[GroupChat] = chats.values.toVector filter (c => c.access == GroupChatAccess.PUBLIC)
def findAllPrivateChatsForUser(id: String) = chats.values.toVector filter (c => def findAllPrivateChatsForUser(id: String) = chats.values.toVector filter (c =>
c.access == GroupChatAccess.PRIVATE && c.isUserMemberOf(id)) c.access == GroupChatAccess.PRIVATE && c.isUserMemberOf(id))
def getAllGroupChatsInMeeting(): Vector[GroupChat] = chats.values.toVector
} }
case class GroupChat(id: String, name: String, access: String, createdBy: GroupChatUser, case class GroupChat(id: String, name: String, access: String, createdBy: GroupChatUser,

View File

@ -56,6 +56,13 @@ object RegisteredUsers {
u u
} }
def updateUserRole(users: RegisteredUsers, user: RegisteredUser,
role: String): RegisteredUser = {
val u = user.modify(_.role).setTo(role)
users.save(u)
u
}
} }
class RegisteredUsers { class RegisteredUsers {

View File

@ -1,6 +1,7 @@
package org.bigbluebutton.core.models package org.bigbluebutton.core.models
import com.softwaremill.quicklens._ import com.softwaremill.quicklens._
import org.bigbluebutton.core.util.TimeUtil
object Users2x { object Users2x {
def findWithIntId(users: Users2x, intId: String): Option[UserState] = { def findWithIntId(users: Users2x, intId: String): Option[UserState] = {
@ -26,6 +27,12 @@ object Users2x {
users.toVector.filter(u => !u.presenter) users.toVector.filter(u => !u.presenter)
} }
def updateLastUserActivity(users: Users2x, u: UserState): UserState = {
val newUserState = modify(u)(_.lastActivityTime).setTo(TimeUtil.timeNowInMs())
users.save(newUserState)
newUserState
}
def changeRole(users: Users2x, u: UserState, newRole: String): UserState = { def changeRole(users: Users2x, u: UserState, newRole: String): UserState = {
val newUserState = modify(u)(_.role).setTo(newRole).modify(_.roleChangedOn).setTo(System.currentTimeMillis()) val newUserState = modify(u)(_.role).setTo(newRole).modify(_.roleChangedOn).setTo(System.currentTimeMillis())
users.save(newUserState) users.save(newUserState)
@ -103,6 +110,10 @@ object Users2x {
users.toVector.find(u => u.role == Roles.MODERATOR_ROLE) users.toVector.find(u => u.role == Roles.MODERATOR_ROLE)
} }
def findAllAuthedUsers(users: Users2x): Vector[UserState] = {
users.toVector.find(u => u.authed).toVector
}
def addUserToPresenterGroup(users: Users2x, userIdToAdd: String): Boolean = { def addUserToPresenterGroup(users: Users2x, userIdToAdd: String): Boolean = {
users.updatePresenterGroup(users.presenterGroup.filterNot(_ == userIdToAdd).:+(userIdToAdd)) // ensure no repetition users.updatePresenterGroup(users.presenterGroup.filterNot(_ == userIdToAdd).:+(userIdToAdd)) // ensure no repetition
users.presenterGroup.contains(userIdToAdd) users.presenterGroup.contains(userIdToAdd)
@ -202,9 +213,22 @@ class Users2x {
case class OldPresenter(userId: String, changedPresenterOn: Long) case class OldPresenter(userId: String, changedPresenterOn: Long)
case class UserState(intId: String, extId: String, name: String, role: String, case class UserState(
guest: Boolean, authed: Boolean, guestStatus: String, emoji: String, locked: Boolean, intId: String,
presenter: Boolean, avatar: String, roleChangedOn: Long = System.currentTimeMillis()) extId: String,
name: String,
role: String,
guest: Boolean,
authed: Boolean,
guestStatus: String,
emoji: String,
locked: Boolean,
presenter: Boolean,
avatar: String,
roleChangedOn: Long = System.currentTimeMillis(),
lastActivityTime: Long = TimeUtil.timeNowInMs(),
clientType: String
)
case class UserIdAndName(id: String, name: String) case class UserIdAndName(id: String, name: String)
@ -232,4 +256,5 @@ object EjectReasonCode {
val EJECT_USER = "user_requested_eject_reason" val EJECT_USER = "user_requested_eject_reason"
val SYSTEM_EJECT_USER = "system_requested_eject_reason" val SYSTEM_EJECT_USER = "system_requested_eject_reason"
val VALIDATE_TOKEN = "validate_token_failed_eject_reason" val VALIDATE_TOKEN = "validate_token_failed_eject_reason"
val USER_INACTIVITY = "user_inactivity_eject_reason"
} }

View File

@ -94,6 +94,8 @@ class ReceivedJsonMsgHandlerActor(
routeGenericMsg[RemoveUserFromPresenterGroupCmdMsg](envelope, jsonNode) routeGenericMsg[RemoveUserFromPresenterGroupCmdMsg](envelope, jsonNode)
case GetPresenterGroupReqMsg.NAME => case GetPresenterGroupReqMsg.NAME =>
routeGenericMsg[GetPresenterGroupReqMsg](envelope, jsonNode) routeGenericMsg[GetPresenterGroupReqMsg](envelope, jsonNode)
case UserActivitySignCmdMsg.NAME =>
routeGenericMsg[UserActivitySignCmdMsg](envelope, jsonNode)
// Poll // Poll
case StartCustomPollReqMsg.NAME => case StartCustomPollReqMsg.NAME =>
@ -184,7 +186,6 @@ class ReceivedJsonMsgHandlerActor(
case GetWhiteboardAnnotationsReqMsg.NAME => case GetWhiteboardAnnotationsReqMsg.NAME =>
routeGenericMsg[GetWhiteboardAnnotationsReqMsg](envelope, jsonNode) routeGenericMsg[GetWhiteboardAnnotationsReqMsg](envelope, jsonNode)
case ClientToServerLatencyTracerMsg.NAME => case ClientToServerLatencyTracerMsg.NAME =>
log.info("-- trace --" + jsonNode.toString)
routeGenericMsg[ClientToServerLatencyTracerMsg](envelope, jsonNode) routeGenericMsg[ClientToServerLatencyTracerMsg](envelope, jsonNode)
// Presentation // Presentation
@ -254,6 +255,8 @@ class ReceivedJsonMsgHandlerActor(
routeGenericMsg[SendPrivateMessagePubMsg](envelope, jsonNode) routeGenericMsg[SendPrivateMessagePubMsg](envelope, jsonNode)
case ClearPublicChatHistoryPubMsg.NAME => case ClearPublicChatHistoryPubMsg.NAME =>
routeGenericMsg[ClearPublicChatHistoryPubMsg](envelope, jsonNode) routeGenericMsg[ClearPublicChatHistoryPubMsg](envelope, jsonNode)
case UserTypingPubMsg.NAME =>
routeGenericMsg[UserTypingPubMsg](envelope, jsonNode)
// Meeting // Meeting
case EndMeetingSysCmdMsg.NAME => case EndMeetingSysCmdMsg.NAME =>
@ -264,6 +267,8 @@ class ReceivedJsonMsgHandlerActor(
routeGenericMsg[LogoutAndEndMeetingCmdMsg](envelope, jsonNode) routeGenericMsg[LogoutAndEndMeetingCmdMsg](envelope, jsonNode)
case SetRecordingStatusCmdMsg.NAME => case SetRecordingStatusCmdMsg.NAME =>
routeGenericMsg[SetRecordingStatusCmdMsg](envelope, jsonNode) routeGenericMsg[SetRecordingStatusCmdMsg](envelope, jsonNode)
case RecordAndClearPreviousMarkersCmdMsg.NAME =>
routeGenericMsg[RecordAndClearPreviousMarkersCmdMsg](envelope, jsonNode)
case GetRecordingStatusReqMsg.NAME => case GetRecordingStatusReqMsg.NAME =>
routeGenericMsg[GetRecordingStatusReqMsg](envelope, jsonNode) routeGenericMsg[GetRecordingStatusReqMsg](envelope, jsonNode)
case GetScreenshareStatusReqMsg.NAME => case GetScreenshareStatusReqMsg.NAME =>

View File

@ -0,0 +1,39 @@
/**
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
*
* Copyright (c) 2017 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 <http://www.gnu.org/licenses/>.
*
*/
package org.bigbluebutton.core.record.events
class RecordStatusResetEvent extends AbstractParticipantRecordEvent {
import RecordStatusResetEvent._
setEvent("RecordStatusReset")
def setUserId(userId: String) {
eventMap.put(USER_ID, userId)
}
def setRecordingStatus(status: Boolean) {
eventMap.put(STATUS, status.toString)
}
}
object RecordStatusResetEvent {
protected final val USER_ID = "userId"
protected final val STATUS = "status"
}

View File

@ -9,6 +9,7 @@ import org.bigbluebutton.core.domain.MeetingState2x
import org.bigbluebutton.core.models._ import org.bigbluebutton.core.models._
import org.bigbluebutton.core2.MeetingStatus2x import org.bigbluebutton.core2.MeetingStatus2x
import org.bigbluebutton.core2.message.senders.{ MsgBuilder, UserJoinedMeetingEvtMsgBuilder } import org.bigbluebutton.core2.message.senders.{ MsgBuilder, UserJoinedMeetingEvtMsgBuilder }
import org.bigbluebutton.core.util.TimeUtil
trait HandlerHelpers extends SystemConfiguration { trait HandlerHelpers extends SystemConfiguration {
@ -25,7 +26,7 @@ trait HandlerHelpers extends SystemConfiguration {
outGW.send(event) outGW.send(event)
} }
def userJoinMeeting(outGW: OutMsgRouter, authToken: String, def userJoinMeeting(outGW: OutMsgRouter, authToken: String, clientType: String,
liveMeeting: LiveMeeting, state: MeetingState2x): MeetingState2x = { liveMeeting: LiveMeeting, state: MeetingState2x): MeetingState2x = {
val nu = for { val nu = for {
regUser <- RegisteredUsers.findWithToken(authToken, liveMeeting.registeredUsers) regUser <- RegisteredUsers.findWithToken(authToken, liveMeeting.registeredUsers)
@ -41,7 +42,8 @@ trait HandlerHelpers extends SystemConfiguration {
emoji = "none", emoji = "none",
presenter = false, presenter = false,
locked = MeetingStatus2x.getPermissions(liveMeeting.status).lockOnJoin, locked = MeetingStatus2x.getPermissions(liveMeeting.status).lockOnJoin,
avatar = regUser.avatarURL avatar = regUser.avatarURL,
clientType = clientType
) )
} }
@ -51,23 +53,36 @@ trait HandlerHelpers extends SystemConfiguration {
val event = UserJoinedMeetingEvtMsgBuilder.build(liveMeeting.props.meetingProp.intId, newUser) val event = UserJoinedMeetingEvtMsgBuilder.build(liveMeeting.props.meetingProp.intId, newUser)
outGW.send(event) outGW.send(event)
startRecordingIfAutoStart2x(outGW, liveMeeting) val newState = startRecordingIfAutoStart2x(outGW, liveMeeting, state)
if (!Users2x.hasPresenter(liveMeeting.users2x)) { if (!Users2x.hasPresenter(liveMeeting.users2x)) {
UsersApp.automaticallyAssignPresenter(outGW, liveMeeting) UsersApp.automaticallyAssignPresenter(outGW, liveMeeting)
} }
state.update(state.expiryTracker.setUserHasJoined()) if (newUser.authed) {
if (!MeetingStatus2x.hasAuthedUserJoined(liveMeeting.status)) {
MeetingStatus2x.authUserHadJoined(liveMeeting.status)
}
if (MeetingStatus2x.getLastAuthedUserLeftOn(liveMeeting.status) > 0) {
MeetingStatus2x.resetLastAuthedUserLeftOn(liveMeeting.status)
}
}
newState.update(newState.expiryTracker.setUserHasJoined())
case None => case None =>
state state
} }
} }
def startRecordingIfAutoStart2x(outGW: OutMsgRouter, liveMeeting: LiveMeeting): Unit = { def startRecordingIfAutoStart2x(outGW: OutMsgRouter, liveMeeting: LiveMeeting, state: MeetingState2x): MeetingState2x = {
var newState = state
if (liveMeeting.props.recordProp.record && !MeetingStatus2x.isRecording(liveMeeting.status) && if (liveMeeting.props.recordProp.record && !MeetingStatus2x.isRecording(liveMeeting.status) &&
liveMeeting.props.recordProp.autoStartRecording && Users2x.numUsers(liveMeeting.users2x) == 1) { liveMeeting.props.recordProp.autoStartRecording && Users2x.numUsers(liveMeeting.users2x) == 1) {
MeetingStatus2x.recordingStarted(liveMeeting.status) MeetingStatus2x.recordingStarted(liveMeeting.status)
val tracker = state.recordingTracker.startTimer(TimeUtil.timeNowInMs())
def buildRecordingStatusChangedEvtMsg(meetingId: String, userId: String, recording: Boolean): BbbCommonEnvCoreMsg = { def buildRecordingStatusChangedEvtMsg(meetingId: String, userId: String, recording: Boolean): BbbCommonEnvCoreMsg = {
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, userId) val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, userId)
val envelope = BbbCoreEnvelope(RecordingStatusChangedEvtMsg.NAME, routing) val envelope = BbbCoreEnvelope(RecordingStatusChangedEvtMsg.NAME, routing)
@ -83,8 +98,38 @@ trait HandlerHelpers extends SystemConfiguration {
"system", MeetingStatus2x.isRecording(liveMeeting.status) "system", MeetingStatus2x.isRecording(liveMeeting.status)
) )
outGW.send(event) outGW.send(event)
newState = state.update(tracker)
} }
newState
}
def stopRecordingIfAutoStart2x(outGW: OutMsgRouter, liveMeeting: LiveMeeting, state: MeetingState2x): MeetingState2x = {
var newState = state
if (liveMeeting.props.recordProp.record && MeetingStatus2x.isRecording(liveMeeting.status) &&
liveMeeting.props.recordProp.autoStartRecording && Users2x.numUsers(liveMeeting.users2x) == 0) {
MeetingStatus2x.recordingStopped(liveMeeting.status)
val tracker = state.recordingTracker.pauseTimer(TimeUtil.timeNowInMs())
def buildRecordingStatusChangedEvtMsg(meetingId: String, userId: String, recording: Boolean): BbbCommonEnvCoreMsg = {
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, userId)
val envelope = BbbCoreEnvelope(RecordingStatusChangedEvtMsg.NAME, routing)
val body = RecordingStatusChangedEvtMsgBody(recording, userId)
val header = BbbClientMsgHeader(RecordingStatusChangedEvtMsg.NAME, meetingId, userId)
val event = RecordingStatusChangedEvtMsg(header, body)
BbbCommonEnvCoreMsg(envelope, event)
}
val event = buildRecordingStatusChangedEvtMsg(
liveMeeting.props.meetingProp.intId,
"system", MeetingStatus2x.isRecording(liveMeeting.status)
)
outGW.send(event)
newState = state.update(tracker)
}
newState
} }
def endMeeting(outGW: OutMsgRouter, liveMeeting: LiveMeeting, reason: String): Unit = { def endMeeting(outGW: OutMsgRouter, liveMeeting: LiveMeeting, reason: String): Unit = {

View File

@ -4,11 +4,12 @@ import java.io.{ PrintWriter, StringWriter }
import akka.actor._ import akka.actor._
import akka.actor.SupervisorStrategy.Resume import akka.actor.SupervisorStrategy.Resume
import org.bigbluebutton.core.apps.groupchats.{ GroupChatApp, GroupChatHdlrs } import org.bigbluebutton.SystemConfiguration
import org.bigbluebutton.core.apps.groupchats.GroupChatHdlrs
import org.bigbluebutton.core.apps.presentationpod._ import org.bigbluebutton.core.apps.presentationpod._
import org.bigbluebutton.core.apps.users._ import org.bigbluebutton.core.apps.users._
import org.bigbluebutton.core.apps.whiteboard.ClientToServerLatencyTracerMsgHdlr import org.bigbluebutton.core.apps.whiteboard.ClientToServerLatencyTracerMsgHdlr
import org.bigbluebutton.core.domain.{ MeetingExpiryTracker, MeetingInactivityTracker, MeetingState2x } import org.bigbluebutton.core.domain._
import org.bigbluebutton.core.util.TimeUtil import org.bigbluebutton.core.util.TimeUtil
import org.bigbluebutton.common2.domain.DefaultProps import org.bigbluebutton.common2.domain.DefaultProps
import org.bigbluebutton.core.api._ import org.bigbluebutton.core.api._
@ -34,16 +35,14 @@ import scala.concurrent.duration._
import org.bigbluebutton.core.apps.layout.LayoutApp2x import org.bigbluebutton.core.apps.layout.LayoutApp2x
import org.bigbluebutton.core.apps.meeting.{ SyncGetMeetingInfoRespMsgHdlr, ValidateConnAuthTokenSysMsgHdlr } import org.bigbluebutton.core.apps.meeting.{ SyncGetMeetingInfoRespMsgHdlr, ValidateConnAuthTokenSysMsgHdlr }
import org.bigbluebutton.core.apps.users.ChangeLockSettingsInMeetingCmdMsgHdlr import org.bigbluebutton.core.apps.users.ChangeLockSettingsInMeetingCmdMsgHdlr
import org.bigbluebutton.core2.message.senders.MsgBuilder import org.bigbluebutton.core2.message.senders.{ MsgBuilder, Sender }
import org.bigbluebutton.core2.testdata.FakeTestData
object MeetingActor { object MeetingActor {
def props( def props(
props: DefaultProps, props: DefaultProps,
eventBus: InternalEventBus, eventBus: InternalEventBus,
outGW: OutMsgRouter, outGW: OutMsgRouter,
liveMeeting: LiveMeeting liveMeeting: LiveMeeting): Props =
): Props =
Props(classOf[MeetingActor], props, eventBus, outGW, liveMeeting) Props(classOf[MeetingActor], props, eventBus, outGW, liveMeeting)
} }
@ -51,34 +50,35 @@ class MeetingActor(
val props: DefaultProps, val props: DefaultProps,
val eventBus: InternalEventBus, val eventBus: InternalEventBus,
val outGW: OutMsgRouter, val outGW: OutMsgRouter,
val liveMeeting: LiveMeeting val liveMeeting: LiveMeeting)
) extends BaseMeetingActor
extends BaseMeetingActor with SystemConfiguration
with GuestsApp with GuestsApp
with LayoutApp2x with LayoutApp2x
with VoiceApp2x with VoiceApp2x
with BreakoutApp2x with BreakoutApp2x
with UsersApp2x with UsersApp2x
with UserBroadcastCamStartMsgHdlr with UserBroadcastCamStartMsgHdlr
with UserJoinMeetingReqMsgHdlr with UserJoinMeetingReqMsgHdlr
with UserJoinMeetingAfterReconnectReqMsgHdlr with UserJoinMeetingAfterReconnectReqMsgHdlr
with UserBroadcastCamStopMsgHdlr with UserBroadcastCamStopMsgHdlr
with UserConnectedToGlobalAudioMsgHdlr with UserConnectedToGlobalAudioMsgHdlr
with UserDisconnectedFromGlobalAudioMsgHdlr with UserDisconnectedFromGlobalAudioMsgHdlr
with MuteAllExceptPresentersCmdMsgHdlr with MuteAllExceptPresentersCmdMsgHdlr
with MuteMeetingCmdMsgHdlr with MuteMeetingCmdMsgHdlr
with IsMeetingMutedReqMsgHdlr with IsMeetingMutedReqMsgHdlr
with EjectUserFromVoiceCmdMsgHdlr with EjectUserFromVoiceCmdMsgHdlr
with EndMeetingSysCmdMsgHdlr with EndMeetingSysCmdMsgHdlr
with DestroyMeetingSysCmdMsgHdlr with DestroyMeetingSysCmdMsgHdlr
with SendTimeRemainingUpdateHdlr with SendTimeRemainingUpdateHdlr
with SendBreakoutTimeRemainingMsgHdlr with SendBreakoutTimeRemainingMsgHdlr
with ChangeLockSettingsInMeetingCmdMsgHdlr with ChangeLockSettingsInMeetingCmdMsgHdlr
with SyncGetMeetingInfoRespMsgHdlr with SyncGetMeetingInfoRespMsgHdlr
with ClientToServerLatencyTracerMsgHdlr with ClientToServerLatencyTracerMsgHdlr
with ValidateConnAuthTokenSysMsgHdlr { with ValidateConnAuthTokenSysMsgHdlr
with UserActivitySignCmdMsgHdlr {
override val supervisorStrategy = OneForOneStrategy(maxNrOfRetries = 10, withinTimeRange = 1 minute) { override val supervisorStrategy = OneForOneStrategy(maxNrOfRetries = 10, withinTimeRange = 1 minute) {
case e: Exception => { case e: Exception => {
@ -96,8 +96,7 @@ class MeetingActor(
*/ */
var actorMonitor = context.actorOf( var actorMonitor = context.actorOf(
MeetingActorAudit.props(props, eventBus, outGW), MeetingActorAudit.props(props, eventBus, outGW),
"actorMonitor-" + props.meetingProp.intId "actorMonitor-" + props.meetingProp.intId)
)
val msgBus = MessageBus(eventBus, outGW) val msgBus = MessageBus(eventBus, outGW)
@ -119,40 +118,45 @@ class MeetingActor(
TimeUtil.minutesToMillis(props.durationProps.warnMinutesBeforeMax), TimeUtil.minutesToMillis(props.durationProps.warnMinutesBeforeMax),
lastActivityTimestampInMs = TimeUtil.timeNowInMs(), lastActivityTimestampInMs = TimeUtil.timeNowInMs(),
warningSent = false, warningSent = false,
warningSentOnTimestampInMs = 0L warningSentOnTimestampInMs = 0L)
)
val expiryTracker = new MeetingExpiryTracker( val expiryTracker = new MeetingExpiryTracker(
startedOnInMs = TimeUtil.timeNowInMs(), startedOnInMs = TimeUtil.timeNowInMs(),
userHasJoined = false, userHasJoined = false,
isBreakout = props.meetingProp.isBreakout,
lastUserLeftOnInMs = None, lastUserLeftOnInMs = None,
durationInMs = TimeUtil.minutesToMillis(props.durationProps.duration), durationInMs = TimeUtil.minutesToMillis(props.durationProps.duration),
meetingExpireIfNoUserJoinedInMs = TimeUtil.minutesToMillis(props.durationProps.meetingExpireIfNoUserJoinedInMinutes), meetingExpireIfNoUserJoinedInMs = TimeUtil.minutesToMillis(props.durationProps.meetingExpireIfNoUserJoinedInMinutes),
meetingExpireWhenLastUserLeftInMs = TimeUtil.minutesToMillis(props.durationProps.meetingExpireWhenLastUserLeftInMinutes) meetingExpireWhenLastUserLeftInMs = TimeUtil.minutesToMillis(props.durationProps.meetingExpireWhenLastUserLeftInMinutes),
) userInactivityInspectTimerInMs = TimeUtil.minutesToMillis(props.durationProps.userInactivityInspectTimerInMinutes),
userInactivityThresholdInMs = TimeUtil.minutesToMillis(props.durationProps.userInactivityInspectTimerInMinutes),
userActivitySignResponseDelayInMs = TimeUtil.minutesToMillis(props.durationProps.userActivitySignResponseDelayInMinutes))
val recordingTracker = new MeetingRecordingTracker(startedOnInMs = 0L, previousDurationInMs = 0L, currentDurationInMs = 0L)
var state = new MeetingState2x( var state = new MeetingState2x(
new GroupChats(Map.empty), new GroupChats(Map.empty),
new PresentationPodManager(Map.empty), new PresentationPodManager(Map.empty),
None, None,
inactivityTracker, inactivityTracker,
expiryTracker expiryTracker,
) recordingTracker)
var lastRttTestSentOn = System.currentTimeMillis() var lastRttTestSentOn = System.currentTimeMillis()
// Create a default publish group chat // Create a default public group chat
state = GroupChatApp.createDefaultPublicGroupChat(GroupChatApp.MAIN_PUBLIC_CHAT, state) state = groupChatApp.handleCreateDefaultPublicGroupChat(state, liveMeeting, msgBus)
//state = GroupChatApp.genTestChatMsgHistory(GroupChatApp.MAIN_PUBLIC_CHAT, state, BbbSystemConst.SYSTEM_USER, liveMeeting)
// Create a default publish group chat //state = GroupChatApp.genTestChatMsgHistory(GroupChatApp.MAIN_PUBLIC_CHAT, state, BbbSystemConst.SYSTEM_USER, liveMeeting)
// Create a default public group chat **DEPRECATED, NOT GOING TO WORK ANYMORE**
//state = GroupChatApp.createDefaultPublicGroupChat("TEST_GROUP_CHAT", state) //state = GroupChatApp.createDefaultPublicGroupChat("TEST_GROUP_CHAT", state)
//state = GroupChatApp.genTestChatMsgHistory("TEST_GROUP_CHAT", state, BbbSystemConst.SYSTEM_USER, liveMeeting) //state = GroupChatApp.genTestChatMsgHistory("TEST_GROUP_CHAT", state, BbbSystemConst.SYSTEM_USER, liveMeeting)
log.debug("NUM GROUP CHATS = " + state.groupChats.findAllPublicChats().length) log.debug("NUM GROUP CHATS = " + state.groupChats.findAllPublicChats().length)
// Create a default Presentation Pod // Create a default Presentation Pod
state = PresentationPodsApp.createDefaultPresentationPod(state) state = presentationPodsApp.handleCreateDefaultPresentationPod(state, liveMeeting, msgBus)
log.debug("NUM Presentation Pods = " + state.presentationPodManager.getNumberOfPods()) log.debug("NUM Presentation Pods = " + state.presentationPodManager.getNumberOfPods())
// Initialize if the meeting is muted on start // Initialize if the meeting is muted on start
@ -165,11 +169,11 @@ class MeetingActor(
// Set webcamsOnlyForModerator property in case we didn't after meeting creation // Set webcamsOnlyForModerator property in case we didn't after meeting creation
MeetingStatus2x.setWebcamsOnlyForModerator(liveMeeting.status, liveMeeting.props.usersProp.webcamsOnlyForModerator) MeetingStatus2x.setWebcamsOnlyForModerator(liveMeeting.status, liveMeeting.props.usersProp.webcamsOnlyForModerator)
/*******************************************************************/ /** *****************************************************************/
// Helper to create fake users for testing (ralam jan 5, 2018) // Helper to create fake users for testing (ralam jan 5, 2018)
//object FakeTestData extends FakeTestData //object FakeTestData extends FakeTestData
//FakeTestData.createFakeUsers(liveMeeting) //FakeTestData.createFakeUsers(liveMeeting)
/*******************************************************************/ /** *****************************************************************/
def receive = { def receive = {
//============================= //=============================
@ -206,148 +210,216 @@ class MeetingActor(
// Screenshare // Screenshare
case msg: DeskShareGetDeskShareInfoRequest => handleDeskShareGetDeskShareInfoRequest(msg) case msg: DeskShareGetDeskShareInfoRequest => handleDeskShareGetDeskShareInfoRequest(msg)
case _ => // do nothing case msg: SendRecordingTimerInternalMsg =>
state = usersApp.handleSendRecordingTimerInternalMsg(msg, state)
case _ => // do nothing
}
private def updateInactivityTracker(state: MeetingState2x): MeetingState2x = {
val tracker = state.inactivityTracker.updateLastActivityTimestamp(TimeUtil.timeNowInMs())
state.update(tracker)
}
private def updateVoiceUserLastActivity(userId: String) {
for {
vu <- VoiceUsers.findWithVoiceUserId(liveMeeting.voiceUsers, userId)
} yield {
updateUserLastActivity(vu.intId)
}
}
private def updateUserLastActivity(userId: String) {
for {
user <- Users2x.findWithIntId(liveMeeting.users2x, userId)
} yield {
Users2x.updateLastUserActivity(liveMeeting.users2x, user)
}
} }
private def handleBbbCommonEnvCoreMsg(msg: BbbCommonEnvCoreMsg): Unit = { private def handleBbbCommonEnvCoreMsg(msg: BbbCommonEnvCoreMsg): Unit = {
val tracker = state.inactivityTracker.updateLastActivityTimestamp(TimeUtil.timeNowInMs())
state = state.update(tracker)
msg.core match { msg.core match {
case m: EndMeetingSysCmdMsg => handleEndMeeting(m, state) case m: EndMeetingSysCmdMsg => handleEndMeeting(m, state)
// Users // Users
case m: ValidateAuthTokenReqMsg => state = usersApp.handleValidateAuthTokenReqMsg(m, state) case m: ValidateAuthTokenReqMsg => state = usersApp.handleValidateAuthTokenReqMsg(m, state)
case m: UserJoinMeetingReqMsg => state = handleUserJoinMeetingReqMsg(m, state) case m: UserJoinMeetingReqMsg => state = handleUserJoinMeetingReqMsg(m, state)
case m: UserJoinMeetingAfterReconnectReqMsg => state = handleUserJoinMeetingAfterReconnectReqMsg(m, state) case m: UserJoinMeetingAfterReconnectReqMsg => state = handleUserJoinMeetingAfterReconnectReqMsg(m, state)
case m: UserLeaveReqMsg => state = handleUserLeaveReqMsg(m, state) case m: UserLeaveReqMsg => state = handleUserLeaveReqMsg(m, state)
case m: UserBroadcastCamStartMsg => handleUserBroadcastCamStartMsg(m) case m: UserBroadcastCamStartMsg => handleUserBroadcastCamStartMsg(m)
case m: UserBroadcastCamStopMsg => handleUserBroadcastCamStopMsg(m) case m: UserBroadcastCamStopMsg => handleUserBroadcastCamStopMsg(m)
case m: UserJoinedVoiceConfEvtMsg => handleUserJoinedVoiceConfEvtMsg(m) case m: UserJoinedVoiceConfEvtMsg => handleUserJoinedVoiceConfEvtMsg(m)
case m: MeetingActivityResponseCmdMsg => state = usersApp.handleMeetingActivityResponseCmdMsg(m, state) case m: MeetingActivityResponseCmdMsg =>
state = usersApp.handleMeetingActivityResponseCmdMsg(m, state)
state = updateInactivityTracker(state)
case m: LogoutAndEndMeetingCmdMsg => usersApp.handleLogoutAndEndMeetingCmdMsg(m, state) case m: LogoutAndEndMeetingCmdMsg => usersApp.handleLogoutAndEndMeetingCmdMsg(m, state)
case m: SetRecordingStatusCmdMsg => usersApp.handleSetRecordingStatusCmdMsg(m) case m: SetRecordingStatusCmdMsg =>
case m: GetWebcamsOnlyForModeratorReqMsg => usersApp.handleGetWebcamsOnlyForModeratorReqMsg(m) state = usersApp.handleSetRecordingStatusCmdMsg(m, state)
updateUserLastActivity(m.body.setBy)
case m: RecordAndClearPreviousMarkersCmdMsg =>
state = usersApp.handleRecordAndClearPreviousMarkersCmdMsg(m, state)
updateUserLastActivity(m.body.setBy)
case m: GetWebcamsOnlyForModeratorReqMsg => usersApp.handleGetWebcamsOnlyForModeratorReqMsg(m)
case m: UpdateWebcamsOnlyForModeratorCmdMsg => usersApp.handleUpdateWebcamsOnlyForModeratorCmdMsg(m) case m: UpdateWebcamsOnlyForModeratorCmdMsg => usersApp.handleUpdateWebcamsOnlyForModeratorCmdMsg(m)
case m: GetRecordingStatusReqMsg => usersApp.handleGetRecordingStatusReqMsg(m) case m: GetRecordingStatusReqMsg => usersApp.handleGetRecordingStatusReqMsg(m)
case m: ChangeUserEmojiCmdMsg => handleChangeUserEmojiCmdMsg(m) case m: ChangeUserEmojiCmdMsg => handleChangeUserEmojiCmdMsg(m)
// Client requested to eject user // Client requested to eject user
case m: EjectUserFromMeetingCmdMsg => usersApp.handleEjectUserFromMeetingCmdMsg(m) case m: EjectUserFromMeetingCmdMsg =>
usersApp.handleEjectUserFromMeetingCmdMsg(m)
updateUserLastActivity(m.body.ejectedBy)
// Another part of system (e.g. bbb-apps) requested to eject user. // Another part of system (e.g. bbb-apps) requested to eject user.
case m: EjectUserFromMeetingSysMsg => usersApp.handleEjectUserFromMeetingSysMsg(m) case m: EjectUserFromMeetingSysMsg => usersApp.handleEjectUserFromMeetingSysMsg(m)
case m: GetUsersMeetingReqMsg => usersApp.handleGetUsersMeetingReqMsg(m) case m: GetUsersMeetingReqMsg => usersApp.handleGetUsersMeetingReqMsg(m)
case m: ChangeUserRoleCmdMsg => usersApp.handleChangeUserRoleCmdMsg(m) case m: ChangeUserRoleCmdMsg =>
usersApp.handleChangeUserRoleCmdMsg(m)
updateUserLastActivity(m.body.changedBy)
// Whiteboard // Whiteboard
case m: SendCursorPositionPubMsg => wbApp.handle(m, liveMeeting, msgBus) case m: SendCursorPositionPubMsg => wbApp.handle(m, liveMeeting, msgBus)
case m: ClearWhiteboardPubMsg => wbApp.handle(m, liveMeeting, msgBus) case m: ClearWhiteboardPubMsg => wbApp.handle(m, liveMeeting, msgBus)
case m: UndoWhiteboardPubMsg => wbApp.handle(m, liveMeeting, msgBus) case m: UndoWhiteboardPubMsg => wbApp.handle(m, liveMeeting, msgBus)
case m: ModifyWhiteboardAccessPubMsg => wbApp.handle(m, liveMeeting, msgBus) case m: ModifyWhiteboardAccessPubMsg => wbApp.handle(m, liveMeeting, msgBus)
case m: SendWhiteboardAnnotationPubMsg => wbApp.handle(m, liveMeeting, msgBus) case m: SendWhiteboardAnnotationPubMsg => wbApp.handle(m, liveMeeting, msgBus)
case m: GetWhiteboardAnnotationsReqMsg => wbApp.handle(m, liveMeeting, msgBus) case m: GetWhiteboardAnnotationsReqMsg => wbApp.handle(m, liveMeeting, msgBus)
case m: ClientToServerLatencyTracerMsg => handleClientToServerLatencyTracerMsg(m) case m: ClientToServerLatencyTracerMsg => handleClientToServerLatencyTracerMsg(m)
// Poll // Poll
case m: StartPollReqMsg => pollApp.handle(m, state, liveMeeting, msgBus) // passing state but not modifying it case m: StartPollReqMsg =>
case m: StartCustomPollReqMsg => pollApp.handle(m, state, liveMeeting, msgBus) // passing state but not modifying it pollApp.handle(m, state, liveMeeting, msgBus) // passing state but not modifying it
case m: StopPollReqMsg => pollApp.handle(m, state, liveMeeting, msgBus) // passing state but not modifying it updateUserLastActivity(m.body.requesterId)
case m: ShowPollResultReqMsg => pollApp.handle(m, state, liveMeeting, msgBus) // passing state but not modifying it case m: StartCustomPollReqMsg =>
pollApp.handle(m, state, liveMeeting, msgBus) // passing state but not modifying it
updateUserLastActivity(m.body.requesterId)
case m: StopPollReqMsg =>
pollApp.handle(m, state, liveMeeting, msgBus) // passing state but not modifying it
updateUserLastActivity(m.body.requesterId)
case m: ShowPollResultReqMsg =>
pollApp.handle(m, state, liveMeeting, msgBus) // passing state but not modifying it
updateUserLastActivity(m.body.requesterId)
case m: GetCurrentPollReqMsg => pollApp.handle(m, state, liveMeeting, msgBus) // passing state but not modifying it case m: GetCurrentPollReqMsg => pollApp.handle(m, state, liveMeeting, msgBus) // passing state but not modifying it
case m: RespondToPollReqMsg => pollApp.handle(m, liveMeeting, msgBus) case m: RespondToPollReqMsg =>
pollApp.handle(m, liveMeeting, msgBus)
updateUserLastActivity(m.body.requesterId)
// Breakout // Breakout
case m: BreakoutRoomsListMsg => state = handleBreakoutRoomsListMsg(m, state) case m: BreakoutRoomsListMsg => state = handleBreakoutRoomsListMsg(m, state)
case m: CreateBreakoutRoomsCmdMsg => state = handleCreateBreakoutRoomsCmdMsg(m, state) case m: CreateBreakoutRoomsCmdMsg => state = handleCreateBreakoutRoomsCmdMsg(m, state)
case m: EndAllBreakoutRoomsMsg => state = handleEndAllBreakoutRoomsMsg(m, state) case m: EndAllBreakoutRoomsMsg => state = handleEndAllBreakoutRoomsMsg(m, state)
case m: RequestBreakoutJoinURLReqMsg => state = handleRequestBreakoutJoinURLReqMsg(m, state) case m: RequestBreakoutJoinURLReqMsg => state = handleRequestBreakoutJoinURLReqMsg(m, state)
case m: TransferUserToMeetingRequestMsg => state = handleTransferUserToMeetingRequestMsg(m, state) case m: TransferUserToMeetingRequestMsg => state = handleTransferUserToMeetingRequestMsg(m, state)
// Voice // Voice
case m: UserLeftVoiceConfEvtMsg => handleUserLeftVoiceConfEvtMsg(m) case m: UserLeftVoiceConfEvtMsg => handleUserLeftVoiceConfEvtMsg(m)
case m: UserMutedInVoiceConfEvtMsg => handleUserMutedInVoiceConfEvtMsg(m) case m: UserMutedInVoiceConfEvtMsg => handleUserMutedInVoiceConfEvtMsg(m)
case m: UserTalkingInVoiceConfEvtMsg => handleUserTalkingInVoiceConfEvtMsg(m) case m: UserTalkingInVoiceConfEvtMsg =>
state = updateInactivityTracker(state)
updateVoiceUserLastActivity(m.body.voiceUserId)
handleUserTalkingInVoiceConfEvtMsg(m)
case m: RecordingStartedVoiceConfEvtMsg => handleRecordingStartedVoiceConfEvtMsg(m) case m: RecordingStartedVoiceConfEvtMsg => handleRecordingStartedVoiceConfEvtMsg(m)
case m: MuteUserCmdMsg => usersApp.handleMuteUserCmdMsg(m) case m: MuteUserCmdMsg =>
case m: MuteAllExceptPresentersCmdMsg => handleMuteAllExceptPresentersCmdMsg(m) usersApp.handleMuteUserCmdMsg(m)
updateUserLastActivity(m.body.mutedBy)
case m: MuteAllExceptPresentersCmdMsg =>
handleMuteAllExceptPresentersCmdMsg(m)
updateUserLastActivity(m.body.mutedBy)
case m: EjectUserFromVoiceCmdMsg => handleEjectUserFromVoiceCmdMsg(m) case m: EjectUserFromVoiceCmdMsg => handleEjectUserFromVoiceCmdMsg(m)
case m: IsMeetingMutedReqMsg => handleIsMeetingMutedReqMsg(m) case m: IsMeetingMutedReqMsg => handleIsMeetingMutedReqMsg(m)
case m: MuteMeetingCmdMsg => handleMuteMeetingCmdMsg(m) case m: MuteMeetingCmdMsg =>
case m: UserConnectedToGlobalAudioMsg => handleUserConnectedToGlobalAudioMsg(m) handleMuteMeetingCmdMsg(m)
updateUserLastActivity(m.body.mutedBy)
case m: UserConnectedToGlobalAudioMsg => handleUserConnectedToGlobalAudioMsg(m)
case m: UserDisconnectedFromGlobalAudioMsg => handleUserDisconnectedFromGlobalAudioMsg(m) case m: UserDisconnectedFromGlobalAudioMsg => handleUserDisconnectedFromGlobalAudioMsg(m)
case m: VoiceConfRunningEvtMsg => handleVoiceConfRunningEvtMsg(m) case m: VoiceConfRunningEvtMsg => handleVoiceConfRunningEvtMsg(m)
// Layout // Layout
case m: GetCurrentLayoutReqMsg => handleGetCurrentLayoutReqMsg(m) case m: GetCurrentLayoutReqMsg => handleGetCurrentLayoutReqMsg(m)
case m: BroadcastLayoutMsg => handleBroadcastLayoutMsg(m) case m: BroadcastLayoutMsg => handleBroadcastLayoutMsg(m)
// Lock Settings // Lock Settings
case m: ChangeLockSettingsInMeetingCmdMsg => handleSetLockSettings(m) case m: ChangeLockSettingsInMeetingCmdMsg =>
case m: LockUserInMeetingCmdMsg => handleLockUserInMeetingCmdMsg(m) handleSetLockSettings(m)
case m: LockUsersInMeetingCmdMsg => handleLockUsersInMeetingCmdMsg(m) updateUserLastActivity(m.body.setBy)
case m: GetLockSettingsReqMsg => handleGetLockSettingsReqMsg(m) case m: LockUserInMeetingCmdMsg => handleLockUserInMeetingCmdMsg(m)
case m: LockUsersInMeetingCmdMsg => handleLockUsersInMeetingCmdMsg(m)
case m: GetLockSettingsReqMsg => handleGetLockSettingsReqMsg(m)
// Presentation // Presentation
case m: PreuploadedPresentationsSysPubMsg => presentationApp2x.handle(m, liveMeeting, msgBus) case m: PreuploadedPresentationsSysPubMsg => presentationApp2x.handle(m, liveMeeting, msgBus)
case m: AssignPresenterReqMsg => state = handlePresenterChange(m, state) case m: AssignPresenterReqMsg => state = handlePresenterChange(m, state)
// Presentation Pods // Presentation Pods
case m: CreateNewPresentationPodPubMsg => state = presentationPodsApp.handle(m, state, liveMeeting, msgBus) case m: CreateNewPresentationPodPubMsg => state = presentationPodsApp.handle(m, state, liveMeeting, msgBus)
case m: RemovePresentationPodPubMsg => state = presentationPodsApp.handle(m, state, liveMeeting, msgBus) case m: RemovePresentationPodPubMsg => state = presentationPodsApp.handle(m, state, liveMeeting, msgBus)
case m: GetAllPresentationPodsReqMsg => state = presentationPodsApp.handle(m, state, liveMeeting, msgBus) case m: GetAllPresentationPodsReqMsg => state = presentationPodsApp.handle(m, state, liveMeeting, msgBus)
case m: SetCurrentPresentationPubMsg => state = presentationPodsApp.handle(m, state, liveMeeting, msgBus) case m: SetCurrentPresentationPubMsg => state = presentationPodsApp.handle(m, state, liveMeeting, msgBus)
case m: PresentationConversionCompletedSysPubMsg => state = presentationPodsApp.handle(m, state, liveMeeting, msgBus) case m: PresentationConversionCompletedSysPubMsg => state = presentationPodsApp.handle(m, state, liveMeeting, msgBus)
case m: SetCurrentPagePubMsg => state = presentationPodsApp.handle(m, state, liveMeeting, msgBus) case m: SetCurrentPagePubMsg => state = presentationPodsApp.handle(m, state, liveMeeting, msgBus)
case m: SetPresenterInPodReqMsg => state = presentationPodsApp.handle(m, state, liveMeeting, msgBus) case m: SetPresenterInPodReqMsg => state = presentationPodsApp.handle(m, state, liveMeeting, msgBus)
case m: RemovePresentationPubMsg => state = presentationPodsApp.handle(m, state, liveMeeting, msgBus) case m: RemovePresentationPubMsg => state = presentationPodsApp.handle(m, state, liveMeeting, msgBus)
case m: SetPresentationDownloadablePubMsg => state = presentationPodsApp.handle(m, state, liveMeeting, msgBus) case m: SetPresentationDownloadablePubMsg => state = presentationPodsApp.handle(m, state, liveMeeting, msgBus)
case m: PresentationConversionUpdateSysPubMsg => state = presentationPodsApp.handle(m, state, liveMeeting, msgBus) case m: PresentationConversionUpdateSysPubMsg => state = presentationPodsApp.handle(m, state, liveMeeting, msgBus)
case m: PresentationPageGeneratedSysPubMsg => state = presentationPodsApp.handle(m, state, liveMeeting, msgBus) case m: PresentationPageGeneratedSysPubMsg => state = presentationPodsApp.handle(m, state, liveMeeting, msgBus)
case m: PresentationPageCountErrorSysPubMsg => state = presentationPodsApp.handle(m, state, liveMeeting, msgBus) case m: PresentationPageCountErrorSysPubMsg => state = presentationPodsApp.handle(m, state, liveMeeting, msgBus)
case m: PresentationUploadTokenReqMsg => state = presentationPodsApp.handle(m, state, liveMeeting, msgBus) case m: PresentationUploadTokenReqMsg => state = presentationPodsApp.handle(m, state, liveMeeting, msgBus)
case m: ResizeAndMovePagePubMsg => state = presentationPodsApp.handle(m, state, liveMeeting, msgBus) case m: ResizeAndMovePagePubMsg => state = presentationPodsApp.handle(m, state, liveMeeting, msgBus)
// Caption // Caption
case m: EditCaptionHistoryPubMsg => captionApp2x.handle(m, liveMeeting, msgBus) case m: EditCaptionHistoryPubMsg => captionApp2x.handle(m, liveMeeting, msgBus)
case m: UpdateCaptionOwnerPubMsg => captionApp2x.handle(m, liveMeeting, msgBus) case m: UpdateCaptionOwnerPubMsg => captionApp2x.handle(m, liveMeeting, msgBus)
case m: SendCaptionHistoryReqMsg => captionApp2x.handle(m, liveMeeting, msgBus) case m: SendCaptionHistoryReqMsg => captionApp2x.handle(m, liveMeeting, msgBus)
// SharedNotes // SharedNotes
case m: GetSharedNotesPubMsg => sharedNotesApp2x.handle(m, liveMeeting, msgBus) case m: GetSharedNotesPubMsg => sharedNotesApp2x.handle(m, liveMeeting, msgBus)
case m: SyncSharedNotePubMsg => sharedNotesApp2x.handle(m, liveMeeting, msgBus) case m: SyncSharedNotePubMsg => sharedNotesApp2x.handle(m, liveMeeting, msgBus)
case m: ClearSharedNotePubMsg => sharedNotesApp2x.handle(m, liveMeeting, msgBus) case m: ClearSharedNotePubMsg => sharedNotesApp2x.handle(m, liveMeeting, msgBus)
case m: UpdateSharedNoteReqMsg => sharedNotesApp2x.handle(m, liveMeeting, msgBus) case m: UpdateSharedNoteReqMsg => sharedNotesApp2x.handle(m, liveMeeting, msgBus)
case m: CreateSharedNoteReqMsg => sharedNotesApp2x.handle(m, liveMeeting, msgBus) case m: CreateSharedNoteReqMsg => sharedNotesApp2x.handle(m, liveMeeting, msgBus)
case m: DestroySharedNoteReqMsg => sharedNotesApp2x.handle(m, liveMeeting, msgBus) case m: DestroySharedNoteReqMsg => sharedNotesApp2x.handle(m, liveMeeting, msgBus)
// Guests // Guests
case m: GetGuestsWaitingApprovalReqMsg => handleGetGuestsWaitingApprovalReqMsg(m) case m: GetGuestsWaitingApprovalReqMsg => handleGetGuestsWaitingApprovalReqMsg(m)
case m: SetGuestPolicyCmdMsg => handleSetGuestPolicyMsg(m) case m: SetGuestPolicyCmdMsg => handleSetGuestPolicyMsg(m)
case m: GuestsWaitingApprovedMsg => handleGuestsWaitingApprovedMsg(m) case m: GuestsWaitingApprovedMsg => handleGuestsWaitingApprovedMsg(m)
case m: GetGuestPolicyReqMsg => handleGetGuestPolicyReqMsg(m) case m: GetGuestPolicyReqMsg => handleGetGuestPolicyReqMsg(m)
// Chat // Chat
case m: GetChatHistoryReqMsg => chatApp2x.handle(m, liveMeeting, msgBus) case m: GetChatHistoryReqMsg => chatApp2x.handle(m, liveMeeting, msgBus)
case m: SendPublicMessagePubMsg => chatApp2x.handle(m, liveMeeting, msgBus) case m: SendPublicMessagePubMsg =>
case m: SendPrivateMessagePubMsg => chatApp2x.handle(m, liveMeeting, msgBus) chatApp2x.handle(m, liveMeeting, msgBus)
case m: ClearPublicChatHistoryPubMsg => state = chatApp2x.handle(m, state, liveMeeting, msgBus) updateUserLastActivity(m.body.message.fromUserId)
case m: SendPrivateMessagePubMsg =>
chatApp2x.handle(m, liveMeeting, msgBus)
updateUserLastActivity(m.body.message.fromUserId)
case m: ClearPublicChatHistoryPubMsg => state = chatApp2x.handle(m, state, liveMeeting, msgBus)
case m: UserTypingPubMsg => chatApp2x.handle(m, liveMeeting, msgBus)
// Screenshare // Screenshare
case m: ScreenshareStartedVoiceConfEvtMsg => screenshareApp2x.handle(m, liveMeeting, msgBus) case m: ScreenshareStartedVoiceConfEvtMsg => screenshareApp2x.handle(m, liveMeeting, msgBus)
case m: ScreenshareStoppedVoiceConfEvtMsg => screenshareApp2x.handle(m, liveMeeting, msgBus) case m: ScreenshareStoppedVoiceConfEvtMsg => screenshareApp2x.handle(m, liveMeeting, msgBus)
case m: ScreenshareRtmpBroadcastStartedVoiceConfEvtMsg => screenshareApp2x.handle(m, liveMeeting, msgBus) case m: ScreenshareRtmpBroadcastStartedVoiceConfEvtMsg => screenshareApp2x.handle(m, liveMeeting, msgBus)
case m: ScreenshareRtmpBroadcastStoppedVoiceConfEvtMsg => screenshareApp2x.handle(m, liveMeeting, msgBus) case m: ScreenshareRtmpBroadcastStoppedVoiceConfEvtMsg => screenshareApp2x.handle(m, liveMeeting, msgBus)
case m: GetScreenshareStatusReqMsg => screenshareApp2x.handle(m, liveMeeting, msgBus) case m: GetScreenshareStatusReqMsg => screenshareApp2x.handle(m, liveMeeting, msgBus)
// GroupChat // GroupChat
case m: CreateGroupChatReqMsg => state = groupChatApp.handle(m, state, liveMeeting, msgBus) case m: CreateGroupChatReqMsg =>
state = groupChatApp.handle(m, state, liveMeeting, msgBus)
updateUserLastActivity(m.header.userId)
case m: GetGroupChatMsgsReqMsg => state = groupChatApp.handle(m, state, liveMeeting, msgBus) case m: GetGroupChatMsgsReqMsg => state = groupChatApp.handle(m, state, liveMeeting, msgBus)
case m: GetGroupChatsReqMsg => state = groupChatApp.handle(m, state, liveMeeting, msgBus) case m: GetGroupChatsReqMsg => state = groupChatApp.handle(m, state, liveMeeting, msgBus)
case m: SendGroupChatMessageMsg => state = groupChatApp.handle(m, state, liveMeeting, msgBus) case m: SendGroupChatMessageMsg =>
state = groupChatApp.handle(m, state, liveMeeting, msgBus)
updateUserLastActivity(m.body.msg.sender.id)
case m: ValidateConnAuthTokenSysMsg => handleValidateConnAuthTokenSysMsg(m) case m: ValidateConnAuthTokenSysMsg => handleValidateConnAuthTokenSysMsg(m)
case _ => log.warning("***** Cannot handle " + msg.envelope.name) case m: UserActivitySignCmdMsg => handleUserActivitySignCmdMsg(m)
case _ => log.warning("***** Cannot handle " + msg.envelope.name)
} }
} }
@ -361,9 +433,15 @@ class MeetingActor(
// sync all presentations // sync all presentations
presentationPodsApp.handleSyncGetPresentationPods(state, liveMeeting, msgBus) presentationPodsApp.handleSyncGetPresentationPods(state, liveMeeting, msgBus)
// TODO send all chat // sync all group chats and group chat messages
groupChatApp.handleSyncGetGroupChatsInfo(state, liveMeeting, msgBus)
// sync all voice users
handleSyncGetVoiceUsersMsg(state, liveMeeting, msgBus)
// TODO send all lock settings // TODO send all lock settings
// TODO send all screen sharing info // TODO send all screen sharing info
} }
def handlePresenterChange(msg: AssignPresenterReqMsg, state: MeetingState2x): MeetingState2x = { def handlePresenterChange(msg: AssignPresenterReqMsg, state: MeetingState2x): MeetingState2x = {
@ -377,8 +455,7 @@ class MeetingActor(
screenshareApp2x.handleScreenshareStoppedVoiceConfEvtMsg( screenshareApp2x.handleScreenshareStoppedVoiceConfEvtMsg(
liveMeeting.props.voiceProp.voiceConf, liveMeeting.props.voiceProp.voiceConf,
liveMeeting.props.screenshareProps.screenshareConf, liveMeeting.props.screenshareProps.screenshareConf,
liveMeeting, msgBus liveMeeting, msgBus)
)
newState newState
@ -409,6 +486,10 @@ class MeetingActor(
sendRttTraceTest() sendRttTraceTest()
setRecordingChapterBreak() setRecordingChapterBreak()
processUserInactivityAudit()
checkIfNeetToEndMeetingWhenNoAuthedUsers(liveMeeting)
} }
var lastRecBreakSentOn = expiryTracker.startedOnInMs var lastRecBreakSentOn = expiryTracker.startedOnInMs
@ -452,6 +533,25 @@ class MeetingActor(
} }
private def checkIfNeetToEndMeetingWhenNoAuthedUsers(liveMeeting: LiveMeeting): Unit = {
val authUserJoined = MeetingStatus2x.hasAuthedUserJoined(liveMeeting.status)
if (endMeetingWhenNoMoreAuthedUsers &&
!liveMeeting.props.meetingProp.isBreakout &&
authUserJoined) {
val lastAuthedUserLeftLimitMs = TimeUtil.timeNowInMs() - MeetingStatus2x.getLastAuthedUserLeftOn(liveMeeting.status)
if (lastAuthedUserLeftLimitMs > TimeUtil.minutesToMillis(endMeetingWhenNoMoreAuthedUsersAfterMinutes)) {
val authedUsers = Users2x.findAllAuthedUsers(liveMeeting.users2x)
if (authedUsers.isEmpty) {
sendEndMeetingDueToExpiry(
MeetingEndReason.ENDED_DUE_TO_NO_AUTHED_USER,
eventBus, outGW, liveMeeting)
}
}
}
}
def handleExtendMeetingDuration(msg: ExtendMeetingDuration) { def handleExtendMeetingDuration(msg: ExtendMeetingDuration) {
} }
@ -474,8 +574,7 @@ class MeetingActor(
val event = buildRecordingStatusChangedEvtMsg( val event = buildRecordingStatusChangedEvtMsg(
liveMeeting.props.meetingProp.intId, liveMeeting.props.meetingProp.intId,
"system", MeetingStatus2x.isRecording(liveMeeting.status) "system", MeetingStatus2x.isRecording(liveMeeting.status))
)
outGW.send(event) outGW.send(event)
} }
@ -499,10 +598,50 @@ class MeetingActor(
val event = buildRecordingStatusChangedEvtMsg( val event = buildRecordingStatusChangedEvtMsg(
liveMeeting.props.meetingProp.intId, liveMeeting.props.meetingProp.intId,
"system", MeetingStatus2x.isRecording(liveMeeting.status) "system", MeetingStatus2x.isRecording(liveMeeting.status))
)
outGW.send(event) outGW.send(event)
} }
} }
var lastUserInactivityInspectSentOn = TimeUtil.timeNowInMs()
var checkInactiveUsers = false
def processUserInactivityAudit(): Unit = {
val now = TimeUtil.timeNowInMs()
// Time to do a new check?
if (now > lastUserInactivityInspectSentOn + expiryTracker.userInactivityInspectTimerInMs) {
lastUserInactivityInspectSentOn = now
checkInactiveUsers = true
warnPotentiallyInactiveUsers()
}
if (checkInactiveUsers && now > lastUserInactivityInspectSentOn + expiryTracker.userActivitySignResponseDelayInMs) {
checkInactiveUsers = false
disconnectInactiveUsers()
}
}
def warnPotentiallyInactiveUsers(): Unit = {
log.info("Checking for inactive users.")
val users = Users2x.findAll(liveMeeting.users2x)
users foreach { u =>
val active = (lastUserInactivityInspectSentOn - expiryTracker.userInactivityThresholdInMs) < u.lastActivityTime
if (!active) {
Sender.sendUserInactivityInspectMsg(liveMeeting.props.meetingProp.intId, u.intId, TimeUtil.minutesToSeconds(props.durationProps.userActivitySignResponseDelayInMinutes), outGW)
}
}
}
def disconnectInactiveUsers(): Unit = {
log.info("Check for users who haven't responded to user inactivity warning.")
val users = Users2x.findAll(liveMeeting.users2x)
users foreach { u =>
val respondedOntIme = (lastUserInactivityInspectSentOn - expiryTracker.userInactivityThresholdInMs) < u.lastActivityTime && (lastUserInactivityInspectSentOn + expiryTracker.userActivitySignResponseDelayInMs) > u.lastActivityTime
if (!respondedOntIme) {
UsersApp.ejectUserFromMeeting(outGW, liveMeeting, u.intId, SystemUser.ID, "User inactive for too long.", EjectReasonCode.USER_INACTIVITY)
Sender.sendDisconnectClientSysMsg(liveMeeting.props.meetingProp.intId, u.intId, SystemUser.ID, EjectReasonCode.USER_INACTIVITY, outGW)
}
}
}
} }

View File

@ -80,6 +80,11 @@ class MeetingActorAudit(
props.meetingProp.intId, props.meetingProp.intId,
SendBreakoutUsersAuditInternalMsg(props.breakoutProps.parentId, props.meetingProp.intId) SendBreakoutUsersAuditInternalMsg(props.breakoutProps.parentId, props.meetingProp.intId)
)) ))
// Trigger recording timer, only for meeting allowing recording
if (props.recordProp.record) {
eventBus.publish(BigBlueButtonEvent(props.meetingProp.intId, SendRecordingTimerInternalMsg(props.meetingProp.intId)))
}
} }
} }

View File

@ -74,8 +74,18 @@ class AnalyticsActor extends Actor with ActorLogging {
case m: MeetingInactivityWarningEvtMsg => logMessage(msg) case m: MeetingInactivityWarningEvtMsg => logMessage(msg)
case m: StartRecordingVoiceConfSysMsg => logMessage(msg) case m: StartRecordingVoiceConfSysMsg => logMessage(msg)
case m: StopRecordingVoiceConfSysMsg => logMessage(msg) case m: StopRecordingVoiceConfSysMsg => logMessage(msg)
//case m: UpdateRecordingTimerEvtMsg => logMessage(msg)
case m: RecordAndClearPreviousMarkersCmdMsg => logMessage(msg)
case m: TransferUserToVoiceConfSysMsg => logMessage(msg) case m: TransferUserToVoiceConfSysMsg => logMessage(msg)
case m: UserBroadcastCamStartMsg => logMessage(msg)
case m: UserBroadcastCamStopMsg => logMessage(msg)
case m: UserBroadcastCamStoppedEvtMsg => logMessage(msg)
case m: UserBroadcastCamStartedEvtMsg => logMessage(msg)
case m: EjectUserFromMeetingSysMsg => logMessage(msg) case m: EjectUserFromMeetingSysMsg => logMessage(msg)
case m: UserActivitySignCmdMsg => logMessage(msg)
case m: UserInactivityInspectMsg => logMessage(msg)
case m: ChangeUserRoleCmdMsg => logMessage(msg)
// Breakout // Breakout
case m: BreakoutRoomEndedEvtMsg => logMessage(msg) case m: BreakoutRoomEndedEvtMsg => logMessage(msg)

View File

@ -25,6 +25,9 @@ class FromAkkaAppsMsgSenderActor(msgSender: MessageSender)
case SyncGetPresentationPodsRespMsg.NAME => msgSender.send(toHTML5RedisChannel, json) case SyncGetPresentationPodsRespMsg.NAME => msgSender.send(toHTML5RedisChannel, json)
case SyncGetMeetingInfoRespMsg.NAME => msgSender.send(toHTML5RedisChannel, json) case SyncGetMeetingInfoRespMsg.NAME => msgSender.send(toHTML5RedisChannel, json)
case SyncGetUsersMeetingRespMsg.NAME => msgSender.send(toHTML5RedisChannel, json) case SyncGetUsersMeetingRespMsg.NAME => msgSender.send(toHTML5RedisChannel, json)
case SyncGetGroupChatsRespMsg.NAME => msgSender.send(toHTML5RedisChannel, json)
case SyncGetGroupChatMsgsRespMsg.NAME => msgSender.send(toHTML5RedisChannel, json)
case SyncGetVoiceUsersRespMsg.NAME => msgSender.send(toHTML5RedisChannel, json)
// Sent to FreeSWITCH // Sent to FreeSWITCH
case ScreenshareStartRtmpBroadcastVoiceConfMsg.NAME => case ScreenshareStartRtmpBroadcastVoiceConfMsg.NAME =>
@ -75,8 +78,6 @@ class FromAkkaAppsMsgSenderActor(msgSender: MessageSender)
msgSender.send(fromAkkaAppsPresRedisChannel, json) msgSender.send(fromAkkaAppsPresRedisChannel, json)
case RemovePresentationEvtMsg.NAME => case RemovePresentationEvtMsg.NAME =>
msgSender.send(fromAkkaAppsPresRedisChannel, json) msgSender.send(fromAkkaAppsPresRedisChannel, json)
case SetPresentationDownloadableEvtMsg.NAME =>
msgSender.send(fromAkkaAppsPresRedisChannel, json)
case SetCurrentPresentationEvtMsg.NAME => case SetCurrentPresentationEvtMsg.NAME =>
msgSender.send(fromAkkaAppsPresRedisChannel, json) msgSender.send(fromAkkaAppsPresRedisChannel, json)
@ -96,7 +97,7 @@ class FromAkkaAppsMsgSenderActor(msgSender: MessageSender)
//================================================================== //==================================================================
//================================================================== //==================================================================
// Some events are only intended for recording and shouldn't be // Some events are only intended for recording and shouldn't be
// sent past akka-apps // sent past akka-apps
// Poll Record Event // Poll Record Event
case UserRespondedToPollRecordMsg.NAME => case UserRespondedToPollRecordMsg.NAME =>

View File

@ -2,6 +2,8 @@ package org.bigbluebutton.core2
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import org.bigbluebutton.core.util.TimeUtil
case class Permissions( case class Permissions(
disableCam: Boolean = false, disableCam: Boolean = false,
disableMic: Boolean = false, disableMic: Boolean = false,
@ -45,6 +47,13 @@ object MeetingStatus2x {
def recordingStopped(status: MeetingStatus2x) = status.recording = false def recordingStopped(status: MeetingStatus2x) = status.recording = false
def isRecording(status: MeetingStatus2x): Boolean = status.recording def isRecording(status: MeetingStatus2x): Boolean = status.recording
def authUserHadJoined(status: MeetingStatus2x) = status.authedUserHasJoined = true
def hasAuthedUserJoined(status: MeetingStatus2x): Boolean = status.authedUserHasJoined
def setLastAuthedUserLeftOn(status: MeetingStatus2x) = status.lastAuthedUserLeftOn = TimeUtil.timeNowInMs()
def getLastAuthedUserLeftOn(status: MeetingStatus2x): Long = status.lastAuthedUserLeftOn
def resetLastAuthedUserLeftOn(status: MeetingStatus2x) = status.lastAuthedUserLeftOn = 0L
def voiceRecordingStart(status2x: MeetingStatus2x, stream: String): VoiceRecordingStream = { def voiceRecordingStart(status2x: MeetingStatus2x, stream: String): VoiceRecordingStream = {
val vrs = new VoiceRecordingStream(stream, recording = false, createdOn = System.currentTimeMillis, ackedOn = None, stoppedOn = None) val vrs = new VoiceRecordingStream(stream, recording = false, createdOn = System.currentTimeMillis, ackedOn = None, stoppedOn = None)
status2x.voiceRecordings += vrs.stream -> vrs status2x.voiceRecordings += vrs.stream -> vrs
@ -115,6 +124,8 @@ class MeetingStatus2x {
private var webcamsOnlyForModerator = false private var webcamsOnlyForModerator = false
private var authedUserHasJoined = false
private var lastAuthedUserLeftOn = 0L
} }
case class VoiceRecordingStream(stream: String, recording: Boolean, createdOn: Long, ackedOn: Option[Long], stoppedOn: Option[Long]) case class VoiceRecordingStream(stream: String, recording: Boolean, createdOn: Long, ackedOn: Option[Long], stoppedOn: Option[Long])

View File

@ -1,7 +1,7 @@
package org.bigbluebutton.core2.message.handlers package org.bigbluebutton.core2.message.handlers
import org.bigbluebutton.common2.msgs._ import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.api.{ SendTimeRemainingAuditInternalMsg } import org.bigbluebutton.core.api.SendTimeRemainingAuditInternalMsg
import org.bigbluebutton.core.domain.MeetingState2x import org.bigbluebutton.core.domain.MeetingState2x
import org.bigbluebutton.core.running.{ LiveMeeting, OutMsgRouter } import org.bigbluebutton.core.running.{ LiveMeeting, OutMsgRouter }
import org.bigbluebutton.core.util.TimeUtil import org.bigbluebutton.core.util.TimeUtil

View File

@ -156,6 +156,16 @@ object MsgBuilder {
BbbCommonEnvCoreMsg(envelope, event) BbbCommonEnvCoreMsg(envelope, event)
} }
def buildRecordStatusResetSysMsg(meetingId: String, recording: Boolean, setBy: String): BbbCommonEnvCoreMsg = {
val routing = Routing.addMsgToClientRouting(MessageTypes.SYSTEM, meetingId, setBy)
val envelope = BbbCoreEnvelope(RecordStatusResetSysMsg.NAME, routing)
val body = RecordStatusResetSysMsgBody(recording, setBy)
val header = BbbCoreHeaderWithMeetingId(RecordStatusResetSysMsg.NAME, meetingId)
val event = RecordStatusResetSysMsg(header, body)
BbbCommonEnvCoreMsg(envelope, event)
}
def buildDisconnectAllClientsSysMsg(meetingId: String, reason: String): BbbCommonEnvCoreMsg = { def buildDisconnectAllClientsSysMsg(meetingId: String, reason: String): BbbCommonEnvCoreMsg = {
val routing = Routing.addMsgToClientRouting(MessageTypes.SYSTEM, meetingId, "not-used") val routing = Routing.addMsgToClientRouting(MessageTypes.SYSTEM, meetingId, "not-used")
val envelope = BbbCoreEnvelope(DisconnectAllClientsSysMsg.NAME, routing) val envelope = BbbCoreEnvelope(DisconnectAllClientsSysMsg.NAME, routing)
@ -208,6 +218,16 @@ object MsgBuilder {
BbbCommonEnvCoreMsg(envelope, event) BbbCommonEnvCoreMsg(envelope, event)
} }
def buildUserInactivityInspectMsg(meetingId: String, userId: String, responseDelay: Long): BbbCommonEnvCoreMsg = {
val routing = Routing.addMsgToClientRouting(MessageTypes.DIRECT, meetingId, userId)
val envelope = BbbCoreEnvelope(UserInactivityInspectMsg.NAME, routing)
val body = UserInactivityInspectMsgBody(meetingId, responseDelay)
val header = BbbClientMsgHeader(UserInactivityInspectMsg.NAME, meetingId, userId)
val event = UserInactivityInspectMsg(header, body)
BbbCommonEnvCoreMsg(envelope, event)
}
def buildCheckAlivePingSysMsg(system: String, timestamp: Long): BbbCommonEnvCoreMsg = { def buildCheckAlivePingSysMsg(system: String, timestamp: Long): BbbCommonEnvCoreMsg = {
val routing = collection.immutable.HashMap("sender" -> "bbb-apps-akka") val routing = collection.immutable.HashMap("sender" -> "bbb-apps-akka")
val envelope = BbbCoreEnvelope(CheckAlivePongSysMsg.NAME, routing) val envelope = BbbCoreEnvelope(CheckAlivePongSysMsg.NAME, routing)
@ -286,4 +306,15 @@ object MsgBuilder {
val event = StopRecordingVoiceConfSysMsg(header, body) val event = StopRecordingVoiceConfSysMsg(header, body)
BbbCommonEnvCoreMsg(envelope, event) BbbCommonEnvCoreMsg(envelope, event)
} }
def buildCreateNewPresentationPodEvtMsg(meetingId: String, currentPresenterId: String, podId: String, userId: String): BbbCommonEnvCoreMsg = {
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, userId)
val envelope = BbbCoreEnvelope(CreateNewPresentationPodEvtMsg.NAME, routing)
val header = BbbClientMsgHeader(CreateNewPresentationPodEvtMsg.NAME, meetingId, userId)
val body = CreateNewPresentationPodEvtMsgBody(currentPresenterId, podId)
val event = CreateNewPresentationPodEvtMsg(header, body)
BbbCommonEnvCoreMsg(envelope, event)
}
} }

View File

@ -7,17 +7,19 @@ object Sender {
def sendUserEjectedFromMeetingClientEvtMsg(meetingId: String, userId: String, def sendUserEjectedFromMeetingClientEvtMsg(meetingId: String, userId: String,
ejectedBy: String, reason: String, ejectedBy: String, reason: String,
reasonCode: String, outGW: OutMsgRouter): Unit = { reasonCode: String, outGW: OutMsgRouter): Unit = {
val ejectFromMeetingClientEvent = MsgBuilder.buildUserEjectedFromMeetingEvtMsg( val ejectFromMeetingClientEvent = MsgBuilder.buildUserEjectedFromMeetingEvtMsg(meetingId, userId, ejectedBy, reason, reasonCode)
meetingId, userId, ejectedBy, reason, reasonCode
)
outGW.send(ejectFromMeetingClientEvent) outGW.send(ejectFromMeetingClientEvent)
} }
def sendDisconnectClientSysMsg(meetingId: String, userId: String, def sendDisconnectClientSysMsg(meetingId: String, userId: String,
ejectedBy: String, reason: String, outGW: OutMsgRouter): Unit = { ejectedBy: String, reason: String, outGW: OutMsgRouter): Unit = {
val ejectFromMeetingSystemEvent = MsgBuilder.buildDisconnectClientSysMsg( val ejectFromMeetingSystemEvent = MsgBuilder.buildDisconnectClientSysMsg(meetingId, userId, ejectedBy, reason)
meetingId, userId, ejectedBy, reason
)
outGW.send(ejectFromMeetingSystemEvent) outGW.send(ejectFromMeetingSystemEvent)
} }
def sendUserInactivityInspectMsg(meetingId: String, userId: String, responseDelay: Long, outGW: OutMsgRouter): Unit = {
val userInactivityInspectMsg = MsgBuilder.buildUserInactivityInspectMsg(meetingId, userId, responseDelay)
outGW.send(userInactivityInspectMsg)
}
} }

View File

@ -10,8 +10,10 @@ object UserJoinedMeetingEvtMsgBuilder {
val body = UserJoinedMeetingEvtMsgBody(intId = userState.intId, extId = userState.extId, name = userState.name, val body = UserJoinedMeetingEvtMsgBody(intId = userState.intId, extId = userState.extId, name = userState.name,
role = userState.role, guest = userState.guest, authed = userState.authed, role = userState.role, guest = userState.guest, authed = userState.authed,
guestStatus = userState.guestStatus, emoji = userState.emoji, guestStatus = userState.guestStatus,
presenter = userState.presenter, locked = userState.locked, avatar = userState.avatar) emoji = userState.emoji,
presenter = userState.presenter, locked = userState.locked, avatar = userState.avatar,
clientType = userState.clientType)
val event = UserJoinedMeetingEvtMsg(meetingId, userState.intId, body) val event = UserJoinedMeetingEvtMsg(meetingId, userState.intId, body)

View File

@ -68,7 +68,6 @@ trait FakeTestData {
def createFakeUser(liveMeeting: LiveMeeting, regUser: RegisteredUser): UserState = { def createFakeUser(liveMeeting: LiveMeeting, regUser: RegisteredUser): UserState = {
UserState(intId = regUser.id, extId = regUser.externId, name = regUser.name, role = regUser.role, UserState(intId = regUser.id, extId = regUser.externId, name = regUser.name, role = regUser.role,
guest = regUser.guest, authed = regUser.authed, guestStatus = regUser.guestStatus, guest = regUser.guest, authed = regUser.authed, guestStatus = regUser.guestStatus,
emoji = "none", locked = false, presenter = false, avatar = regUser.avatarURL) emoji = "none", locked = false, presenter = false, avatar = regUser.avatarURL, clientType = "unknown")
} }
} }

View File

@ -95,6 +95,7 @@ class RedisRecorderActor(val system: ActorSystem)
// Meeting // Meeting
case m: RecordingStatusChangedEvtMsg => handleRecordingStatusChangedEvtMsg(m) case m: RecordingStatusChangedEvtMsg => handleRecordingStatusChangedEvtMsg(m)
case m: RecordStatusResetSysMsg => handleRecordStatusResetSysMsg(m)
case m: WebcamsOnlyForModeratorChangedEvtMsg => handleWebcamsOnlyForModeratorChangedEvtMsg(m) case m: WebcamsOnlyForModeratorChangedEvtMsg => handleWebcamsOnlyForModeratorChangedEvtMsg(m)
case m: EndAndKickAllSysMsg => handleEndAndKickAllSysMsg(m) case m: EndAndKickAllSysMsg => handleEndAndKickAllSysMsg(m)
@ -463,6 +464,15 @@ class RedisRecorderActor(val system: ActorSystem)
record(msg.header.meetingId, ev.toMap) record(msg.header.meetingId, ev.toMap)
} }
private def handleRecordStatusResetSysMsg(msg: RecordStatusResetSysMsg) {
val ev = new RecordStatusResetEvent()
ev.setMeetingId(msg.header.meetingId)
ev.setUserId(msg.body.setBy)
ev.setRecordingStatus(msg.body.recording)
record(msg.header.meetingId, ev.toMap)
}
private def handleWebcamsOnlyForModeratorChangedEvtMsg(msg: WebcamsOnlyForModeratorChangedEvtMsg) { private def handleWebcamsOnlyForModeratorChangedEvtMsg(msg: WebcamsOnlyForModeratorChangedEvtMsg) {
val ev = new WebcamsOnlyForModeratorRecordEvent() val ev = new WebcamsOnlyForModeratorRecordEvent()

26
akka-bbb-fsesl/Dockerfile Normal file
View File

@ -0,0 +1,26 @@
FROM bbb-fsesl-client AS builder
ARG COMMON_VERSION=0.0.1-SNAPSHOT
COPY . /source
RUN cd /source \
&& find -name build.sbt -exec sed -i "s|\(.*org.bigbluebutton.*bbb-common-message[^\"]*\"[ ]*%[ ]*\)\"[^\"]*\"\(.*\)|\1\"$COMMON_VERSION\"\2|g" {} \; \
&& find -name build.sbt -exec sed -i "s|\(.*org.bigbluebutton.*bbb-fsesl-client[^\"]*\"[ ]*%[ ]*\)\"[^\"]*\"\(.*\)|\1\"$COMMON_VERSION\"\2|g" {} \; \
&& sbt compile
RUN apt-get update \
&& apt-get -y install fakeroot
RUN cd /source \
&& sbt debian:packageBin
FROM openjdk:8-jre-slim-stretch
COPY --from=builder /source/target/*.deb /root/
RUN dpkg -i /root/*.deb
COPY wait-for-it.sh /usr/local/bin/
CMD ["/usr/share/bbb-fsesl-akka/bin/bbb-fsesl-akka"]

View File

@ -1,20 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<configuration> <configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder> <layout class="ch.qos.logback.classic.PatternLayout">
<pattern>%date{ISO8601} %-5level %logger{36} %X{akkaSource} - %msg%n</pattern> <Pattern>%d{"yyyy-MM-dd'T'HH:mm:ss.SSSXXX"} %-5level %logger{35} - %msg%n</Pattern>
</encoder> </layout>
</appender> </appender>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<File>logs/bbb-fsesl-akka.log</File> <File>logs/bbb-fsesl-akka.log</File>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<FileNamePattern>logs/bbb-fsesl-akka.%d{yyyy-MM-dd}.log</FileNamePattern> <FileNamePattern>logs/bbb-fsesl-akka.%d{yyyy-MM-dd}.log</FileNamePattern>
<!-- keep 30 days worth of history --> <!-- keep 14 days worth of history -->
<MaxHistory>5</MaxHistory> <MaxHistory>14</MaxHistory>
</rollingPolicy> </rollingPolicy>
<layout class="ch.qos.logback.classic.PatternLayout"> <layout class="ch.qos.logback.classic.PatternLayout">
<Pattern>%d{"yyyy-MM-dd HH:mm:ss,SSSXXX"} [%thread] %-5level %logger{35} - %msg%n</Pattern> <Pattern>%d{"yyyy-MM-dd'T'HH:mm:ss.SSSXXX"} %-5level %logger{35} - %msg%n</Pattern>
</layout> </layout>
</appender> </appender>

177
akka-bbb-fsesl/wait-for-it.sh Executable file
View File

@ -0,0 +1,177 @@
#!/usr/bin/env bash
# Use this script to test if a given TCP host/port are available
cmdname=$(basename $0)
echoerr() { if [[ $QUIET -ne 1 ]]; then echo "$@" 1>&2; fi }
usage()
{
cat << USAGE >&2
Usage:
$cmdname host:port [-s] [-t timeout] [-- command args]
-h HOST | --host=HOST Host or IP under test
-p PORT | --port=PORT TCP port under test
Alternatively, you specify the host and port as host:port
-s | --strict Only execute subcommand if the test succeeds
-q | --quiet Don't output any status messages
-t TIMEOUT | --timeout=TIMEOUT
Timeout in seconds, zero for no timeout
-- COMMAND ARGS Execute command with args after the test finishes
USAGE
exit 1
}
wait_for()
{
if [[ $TIMEOUT -gt 0 ]]; then
echoerr "$cmdname: waiting $TIMEOUT seconds for $HOST:$PORT"
else
echoerr "$cmdname: waiting for $HOST:$PORT without a timeout"
fi
start_ts=$(date +%s)
while :
do
if [[ $ISBUSY -eq 1 ]]; then
nc -z $HOST $PORT
result=$?
else
(echo > /dev/tcp/$HOST/$PORT) >/dev/null 2>&1
result=$?
fi
if [[ $result -eq 0 ]]; then
end_ts=$(date +%s)
echoerr "$cmdname: $HOST:$PORT is available after $((end_ts - start_ts)) seconds"
break
fi
sleep 1
done
return $result
}
wait_for_wrapper()
{
# In order to support SIGINT during timeout: http://unix.stackexchange.com/a/57692
if [[ $QUIET -eq 1 ]]; then
timeout $BUSYTIMEFLAG $TIMEOUT $0 --quiet --child --host=$HOST --port=$PORT --timeout=$TIMEOUT &
else
timeout $BUSYTIMEFLAG $TIMEOUT $0 --child --host=$HOST --port=$PORT --timeout=$TIMEOUT &
fi
PID=$!
trap "kill -INT -$PID" INT
wait $PID
RESULT=$?
if [[ $RESULT -ne 0 ]]; then
echoerr "$cmdname: timeout occurred after waiting $TIMEOUT seconds for $HOST:$PORT"
fi
return $RESULT
}
# process arguments
while [[ $# -gt 0 ]]
do
case "$1" in
*:* )
hostport=(${1//:/ })
HOST=${hostport[0]}
PORT=${hostport[1]}
shift 1
;;
--child)
CHILD=1
shift 1
;;
-q | --quiet)
QUIET=1
shift 1
;;
-s | --strict)
STRICT=1
shift 1
;;
-h)
HOST="$2"
if [[ $HOST == "" ]]; then break; fi
shift 2
;;
--host=*)
HOST="${1#*=}"
shift 1
;;
-p)
PORT="$2"
if [[ $PORT == "" ]]; then break; fi
shift 2
;;
--port=*)
PORT="${1#*=}"
shift 1
;;
-t)
TIMEOUT="$2"
if [[ $TIMEOUT == "" ]]; then break; fi
shift 2
;;
--timeout=*)
TIMEOUT="${1#*=}"
shift 1
;;
--)
shift
CLI=("$@")
break
;;
--help)
usage
;;
*)
echoerr "Unknown argument: $1"
usage
;;
esac
done
if [[ "$HOST" == "" || "$PORT" == "" ]]; then
echoerr "Error: you need to provide a host and port to test."
usage
fi
TIMEOUT=${TIMEOUT:-15}
STRICT=${STRICT:-0}
CHILD=${CHILD:-0}
QUIET=${QUIET:-0}
# check to see if timeout is from busybox?
# check to see if timeout is from busybox?
TIMEOUT_PATH=$(realpath $(which timeout))
if [[ $TIMEOUT_PATH =~ "busybox" ]]; then
ISBUSY=1
BUSYTIMEFLAG="-t"
else
ISBUSY=0
BUSYTIMEFLAG=""
fi
if [[ $CHILD -gt 0 ]]; then
wait_for
RESULT=$?
exit $RESULT
else
if [[ $TIMEOUT -gt 0 ]]; then
wait_for_wrapper
RESULT=$?
else
wait_for
RESULT=$?
fi
fi
if [[ $CLI != "" ]]; then
if [[ $RESULT -ne 0 && $STRICT -eq 1 ]]; then
echoerr "$cmdname: strict mode, refusing to execute subprocess"
exit $RESULT
fi
exec "${CLI[@]}"
else
exit $RESULT
fi

View File

@ -303,7 +303,7 @@ public class VideoTranscoder extends UntypedActor implements ProcessMonitorObser
ffmpeg = new FFmpegCommand(); ffmpeg = new FFmpegCommand();
ffmpeg.setFFmpegPath(FFMPEG_PATH); ffmpeg.setFFmpegPath(FFMPEG_PATH);
ffmpeg.setInput(input); ffmpeg.setInput(input);
ffmpeg.setProtocolWhitelist("file,udp,rtp");
ffmpeg.setLoglevel("quiet"); ffmpeg.setLoglevel("quiet");
ffmpeg.setOutput(outputLive); ffmpeg.setOutput(outputLive);
ffmpeg.addRtmpOutputConnectionParameter(meetingId); ffmpeg.addRtmpOutputConnectionParameter(meetingId);
@ -570,7 +570,7 @@ public class VideoTranscoder extends UntypedActor implements ProcessMonitorObser
if(currentFFmpegRestartNumber == MAX_RESTARTINGS_NUMBER) { if(currentFFmpegRestartNumber == MAX_RESTARTINGS_NUMBER) {
long timeInterval = System.currentTimeMillis() - lastFFmpegRestartTime; long timeInterval = System.currentTimeMillis() - lastFFmpegRestartTime;
if(timeInterval <= MIN_RESTART_TIME) { if(timeInterval <= MIN_RESTART_TIME) {
System.out.println(" > Max number of ffmpeg restartings reached in " + timeInterval + " miliseconds for " + transcoderId + "'s Video Transcoder." + System.out.println(" > Max number of ffmpeg restartings reached in " + timeInterval + " miliseconds for " + transcoderId + "'s Video Transcoder." +
" Not restating it anymore."); " Not restating it anymore.");
return true; return true;
} }

View File

@ -29,6 +29,8 @@ public class FFmpegCommand {
private int frameRate; private int frameRate;
private String frameSize; private String frameSize;
private String protocolWhitelist;
public FFmpegCommand() { public FFmpegCommand() {
this.args = new HashMap(); this.args = new HashMap();
this.x264Params = new HashMap(); this.x264Params = new HashMap();
@ -82,6 +84,11 @@ public class FFmpegCommand {
comm.add(probeSize); comm.add(probeSize);
} }
if(protocolWhitelist != null && !protocolWhitelist.isEmpty()) {
comm.add("-protocol_whitelist");
comm.add(protocolWhitelist);
}
buildRtmpInput(); buildRtmpInput();
comm.add("-i"); comm.add("-i");
@ -323,6 +330,14 @@ public class FFmpegCommand {
this.frameSize = value; this.frameSize = value;
} }
/**
* Sets protocol elements to be whitelisted
* @param whitelist
*/
public void setProtocolWhitelist(String whitelist) {
this.protocolWhitelist = whitelist;
}
/** /**
* Add parameters for rtmp connections. * Add parameters for rtmp connections.
* The order of parameters is the order they are added * The order of parameters is the order they are added

View File

@ -4,6 +4,7 @@ akka {
receive = on receive = on
} }
} }
loggers = ["akka.event.slf4j.Slf4jLogger"]
loglevel = INFO loglevel = INFO
stdout-loglevel = "INFO" stdout-loglevel = "INFO"

View File

@ -1,20 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<configuration> <configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder> <layout class="ch.qos.logback.classic.PatternLayout">
<pattern>%date{ISO8601} %-5level %logger{36} %X{akkaSource} - %msg%n</pattern> <Pattern>%d{"yyyy-MM-dd'T'HH:mm:ss.SSSXXX"} %-5level %logger{35} - %msg%n</Pattern>
</encoder> </layout>
</appender> </appender>
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<File>logs/bbb-transcode.log</File> <File>logs/bbb-transcode.log</File>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<FileNamePattern>logs/bbb-transcode.%d{yyyy-MM-dd}.log</FileNamePattern> <FileNamePattern>logs/bbb-transcode.%d{yyyy-MM-dd}.log</FileNamePattern>
<!-- keep 30 days worth of history --> <!-- keep 14 days worth of history -->
<MaxHistory>5</MaxHistory> <MaxHistory>14</MaxHistory>
</rollingPolicy> </rollingPolicy>
<layout class="ch.qos.logback.classic.PatternLayout"> <layout class="ch.qos.logback.classic.PatternLayout">
<Pattern>%d{"yyyy-MM-dd HH:mm:ss,SSSXXX"} [%thread] %-5level %logger{35} - %msg%n</Pattern> <Pattern>%d{"yyyy-MM-dd'T'HH:mm:ss.SSSXXX"} %-5level %logger{35} - %msg%n</Pattern>
</layout> </layout>
</appender> </appender>

View File

@ -5,7 +5,7 @@ import scala.concurrent.duration._
import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.ExecutionContext.Implicits.global
import akka.actor.ActorSystem import akka.actor.ActorSystem
import org.bigbluebutton.SystemConfiguration import org.bigbluebutton.SystemConfiguration
import org.bigbluebutton.common.converters.ToJsonEncoder //import org.bigbluebutton.common.converters.ToJsonEncoder
class RedisPublisher(val system: ActorSystem) extends SystemConfiguration { class RedisPublisher(val system: ActorSystem) extends SystemConfiguration {
@ -15,13 +15,13 @@ class RedisPublisher(val system: ActorSystem) extends SystemConfiguration {
// CLIENT LIST on redis-cli // CLIENT LIST on redis-cli
redis.clientSetname("BbbTranscodeAkkaPub") redis.clientSetname("BbbTranscodeAkkaPub")
val encoder = new ToJsonEncoder() //val encoder = new ToJsonEncoder()
def sendPingMessage() { //def sendPingMessage() {
val json = encoder.encodePubSubPingMessage("BbbTranscode", System.currentTimeMillis()) // val json = encoder.encodePubSubPingMessage("BbbTranscode", System.currentTimeMillis())
redis.publish("bigbluebutton:to-bbb-apps:system", json) // redis.publish("bigbluebutton:to-bbb-apps:system", json)
} //}
system.scheduler.schedule(10 seconds, 10 seconds)(sendPingMessage()) //system.scheduler.schedule(10 seconds, 10 seconds)(sendPingMessage())
def publish(channel: String, data: String) { def publish(channel: String, data: String) {
//println("PUBLISH TO [" + channel + "]: \n [" + data + "]") //println("PUBLISH TO [" + channel + "]: \n [" + data + "]")

View File

@ -150,9 +150,7 @@ public String getJoinMeetingURL(String username, String meetingID, String passwo
} }
//
//
// Create a meeting and return a URL to join it as moderator. This is used for the API demos. // Create a meeting and return a URL to join it as moderator. This is used for the API demos.
// //
// Passed // Passed
@ -169,6 +167,32 @@ public String getJoinMeetingURL(String username, String meetingID, String passwo
// Note this meeting will use username for meetingID // Note this meeting will use username for meetingID
public String getJoinURL(String username, String meetingID, String record, String welcome, Map<String, String> metadata, String xml) { public String getJoinURL(String username, String meetingID, String record, String welcome, Map<String, String> metadata, String xml) {
String isHTML5Client = "false";
String isModerator = "true";
return getJoinURLExtended(username, meetingID, record, welcome, metadata, xml, isHTML5Client, isModerator);
}
//
// Create a meeting and return a URL to join it as moderator. This is used for the API demos.
//
// Passed
// - username
// - meetingID
// - record ["true", "false"]
// - welcome message (null causes BigBlueButton to use the default welcome message
// - metadata (passed through when record="true"
// - xml (used for pre-upload of slides)_
// - isHTML5Client ["true", "false"]
// - isModerator ["true", "false"]
//
// Returned
// - valid join URL using the username
//
// Note this meeting will use username for meetingID
public String getJoinURLExtended(String username, String meetingID, String record, String welcome, Map<String, String> metadata, String xml, String isHTML5Client, String isModerator) {
String base_url_create = BigBlueButtonURL + "api/create?"; String base_url_create = BigBlueButtonURL + "api/create?";
String base_url_join = BigBlueButtonURL + "api/join?"; String base_url_join = BigBlueButtonURL + "api/join?";
@ -182,15 +206,15 @@ public String getJoinURL(String username, String meetingID, String record, Strin
if ((xml != null) && !xml.equals("")) { if ((xml != null) && !xml.equals("")) {
xml_param = xml; xml_param = xml;
} }
Random random = new Random(); Random random = new Random();
String voiceBridge_param = "&voiceBridge=" + (70000 + random.nextInt(9999)); String voiceBridge_param = "&voiceBridge=" + (70000 + random.nextInt(9999));
// //
// When creating a meeting, the 'name' parameter is the name of the meeting (not to be confused with // When creating a meeting, the 'name' parameter is the name of the meeting (not to be confused with
// the username). For example, the name could be "Fred's meeting" and the meetingID could be "ID-1234312". // the username). For example, the name could be "Fred's meeting" and the meetingID could be "ID-1234312".
// //
// While name and meetingID should be different, we'll keep them the same. Why? Because calling api/create? // While name and meetingID should be different, we'll keep them the same. Why? Because calling api/create?
// with a previously used meetingID will return same meetingToken (regardless if the meeting is running or not). // with a previously used meetingID will return same meetingToken (regardless if the meeting is running or not).
// //
// This means the first person to call getJoinURL with meetingID="Demo Meeting" will actually create the // This means the first person to call getJoinURL with meetingID="Demo Meeting" will actually create the
@ -199,7 +223,6 @@ public String getJoinURL(String username, String meetingID, String record, Strin
// //
// Note: We're hard-coding the password for moderator and attendee (viewer) for purposes of demo. // Note: We're hard-coding the password for moderator and attendee (viewer) for purposes of demo.
// //
String create_parameters = "name=" + urlEncode(meetingID) String create_parameters = "name=" + urlEncode(meetingID)
+ "&meetingID=" + urlEncode(meetingID) + welcome_param + voiceBridge_param + "&meetingID=" + urlEncode(meetingID) + welcome_param + voiceBridge_param
+ "&attendeePW=ap&moderatorPW=mp" + "&attendeePW=ap&moderatorPW=mp"
@ -207,12 +230,17 @@ public String getJoinURL(String username, String meetingID, String record, Strin
+ "&record=" + record + getMetaData( metadata ); + "&record=" + record + getMetaData( metadata );
String password = "mp"; // Attempt to join as moderator by default
if ("false" == isModerator) {
password = "ap"; // default attendee password
}
// Attempt to create a meeting using meetingID // Attempt to create a meeting using meetingID
Document doc = null; Document doc = null;
try { try {
String url = base_url_create + create_parameters String url = base_url_create + create_parameters
+ "&checksum=" + "&checksum="
+ checksum("create" + create_parameters + salt); + checksum("create" + create_parameters + salt);
doc = parseXml( postURL( url, xml_param ) ); doc = parseXml( postURL( url, xml_param ) );
} catch (Exception e) { } catch (Exception e) {
@ -224,20 +252,24 @@ public String getJoinURL(String username, String meetingID, String record, Strin
// //
// Looks good, now return a URL to join that meeting // Looks good, now return a URL to join that meeting
// //
String join_parameters = "meetingID=" + urlEncode(meetingID) String join_parameters = "meetingID=" + urlEncode(meetingID)
+ "&fullName=" + urlEncode(username) + "&password=mp"; + "&fullName=" + urlEncode(username)
+ "&joinViaHtml5=" + isHTML5Client
+ "&password=" + password;
return base_url_join + join_parameters + "&checksum=" return base_url_join + join_parameters + "&checksum="
+ checksum("join" + join_parameters + salt); + checksum("join" + join_parameters + salt);
} }
return doc.getElementsByTagName("messageKey").item(0).getTextContent() return doc.getElementsByTagName("messageKey").item(0).getTextContent()
.trim() .trim()
+ ": " + ": "
+ doc.getElementsByTagName("message").item(0).getTextContent() + doc.getElementsByTagName("message").item(0).getTextContent()
.trim(); .trim();
} }
@ -967,3 +999,4 @@ public static Element getElementWithAttribute(Node root, String attrName, String
} }
%> %>

View File

@ -70,7 +70,7 @@ if (request.getParameterMap().isEmpty()) {
<td>&nbsp;</td> <td>&nbsp;</td>
<td style="text-align: right; ">Moderator Role:</td> <td style="text-align: right; ">Moderator Role:</td>
<td style="width: 5px; ">&nbsp;</td> <td style="width: 5px; ">&nbsp;</td>
<td style="text-align: left "><input type=checkbox name=isModerator value="true"></td> <td style="text-align: left "><input type=checkbox name=isModerator value="true" checked></td>
<tr> <tr>
<tr> <tr>
@ -88,39 +88,22 @@ if (request.getParameterMap().isEmpty()) {
<% <%
} else if (request.getParameter("action").equals("create")) { } else if (request.getParameter("action").equals("create")) {
//
// Got an action=create
//
String username = request.getParameter("username"); String username = request.getParameter("username");
// set defaults and overwrite them if custom values exist // set defaults and overwrite them if custom values exist
String meetingname = "Demo Meeting"; String meetingname = "Demo Meeting";
if (request.getParameter("meetingname") != null) { if (request.getParameter("meetingname") != null) {
meetingname = request.getParameter("meetingname"); meetingname = request.getParameter("meetingname");
} }
String defaultModeratorPassword = "mp"; Boolean isModerator = new Boolean(false);
String defaultAttendeePassword = "ap"; Boolean isHTML5 = new Boolean(true);
String defaultPassword = defaultAttendeePassword; Boolean isRecorded = new Boolean(true);
boolean isModerator = false;
if (request.getParameter("isModerator") != null) { if (request.getParameter("isModerator") != null) {
isModerator = Boolean.parseBoolean(request.getParameter("isModerator")); isModerator = Boolean.parseBoolean(request.getParameter("isModerator"));
defaultPassword = defaultModeratorPassword;
} }
String ip = BigBlueButtonURL.split("\\/bigbluebutton")[0]; String joinURL = getJoinURLExtended(username, meetingname, isRecorded.toString(), null, null, null, isHTML5.toString(), isModerator.toString());
String html5url = ip + "/html5client/join";
String meetingId = createMeeting( meetingname, null, defaultModeratorPassword, "Welcome moderator! (moderator only message)", defaultAttendeePassword, null, null );
// Check if we have an existing meeting
if( meetingId.startsWith("Error ")) {
meetingId = meetingname;
}
String joinURL = getJoinMeetingURL(username, meetingId, defaultPassword, html5url);
if (joinURL.startsWith("http://") || joinURL.startsWith("https://")) { if (joinURL.startsWith("http://") || joinURL.startsWith("https://")) {
%> %>

View File

@ -0,0 +1,115 @@
<!--
BigBlueButton - http://www.bigbluebutton.org
Copyright (c) 2008-2018 by respective authors (see below). All rights reserved.
BigBlueButton 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 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, If not, see <http://www.gnu.org/licenses/>.
-->
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Join Video Chat</title>
</head>
<body>
<p>You must have the BigBlueButton HTML5 client installed to use this API demo.</p>
<%@ include file="bbb_api.jsp"%>
<%
if (request.getParameterMap().isEmpty()) {
//
// Assume we want to create a meeting
//
%>
<%@ include file="demo_header.jsp"%>
<h2>Video Chat via HTML5 Client</h2>
<FORM NAME="form1" METHOD="GET">
<table cellpadding="5" cellspacing="5" style="width: 400px; ">
<tbody>
<tr>
<td>&nbsp;</td>
<td style="text-align: right; ">Full Name:</td>
<td style="width: 5px; ">&nbsp;</td>
<td style="text-align: left "><input type="text" autofocus required name="username" /></td>
</tr>
<tr>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td>&nbsp;</td>
<td><input type="submit" value="Join" /></td>
<tr>
</tbody>
</table>
<INPUT TYPE=hidden NAME=action VALUE="create">
</FORM>
<%
} else if (request.getParameter("action").equals("create")) {
String username = request.getParameter("username");
String meetingname = "Video Chat Meeting";
//metadata
Map<String,String> metadata=new HashMap<String,String>();
metadata.put("html5autoswaplayout", "true");
metadata.put("html5autosharewebcam", "true");
metadata.put("html5hidepresentation", "true");
Boolean isModerator = new Boolean(true);
Boolean isHTML5 = new Boolean(true);
Boolean isRecorded = new Boolean(true);
String joinURL = getJoinURLExtended(username, meetingname, isRecorded.toString(), null, metadata, null, isHTML5.toString(), isModerator.toString());
if (joinURL.startsWith("http://") || joinURL.startsWith("https://")) {
%>
<script language="javascript" type="text/javascript">
window.location.href="<%=joinURL%>";
</script>
<%
} else {
%>
Error: getJoinURL() failed
<p/>
<%=joinURL %>
<%
}
}
%>
<%@ include file="demo_footer.jsp"%>
</body>
</html>

View File

@ -20,3 +20,5 @@
<a href="demoHTML5.jsp">HTML5 Client Demo</a> &nbsp;&nbsp; <a href="demoHTML5.jsp">HTML5 Client Demo</a> &nbsp;&nbsp;
<a href="demoHTML5Video.jsp">HTML5 Client Video Chat</a> &nbsp;&nbsp;

View File

@ -101,7 +101,7 @@ You must have the BigBlueButton mobile client installed on your device for this
<% <%
} else if (joinURL.startsWith("https://")) { } else if (joinURL.startsWith("https://")) {
joinURL = joinURL.replace("https", "bigbluebutton"); joinURL = joinURL.replace("https", "bigbluebuttons");
%> %>
<script language="javascript" type="text/javascript"> <script language="javascript" type="text/javascript">

View File

@ -5,6 +5,5 @@ public interface IClientInGW {
void connect(ConnInfo connInfo); void connect(ConnInfo connInfo);
void disconnect(ConnInfo connInfo); void disconnect(ConnInfo connInfo);
void handleMsgFromClient(ConnInfo connInfo, String json); void handleMsgFromClient(ConnInfo connInfo, String json);
void send(String channel, String json);
} }

View File

@ -15,35 +15,26 @@ class ClientGWApplication(val msgToClientGW: MsgToClientGW) extends SystemConfig
val log = Logging(system, getClass) val log = Logging(system, getClass)
log.debug("*********** meetingManagerChannel = " + meetingManagerChannel)
private val msgFromClientEventBus = new MsgFromClientEventBus private val msgFromClientEventBus = new MsgFromClientEventBus
private val jsonMsgToAkkaAppsBus = new JsonMsgToAkkaAppsBus
private val msgFromAkkaAppsEventBus = new MsgFromAkkaAppsEventBus private val msgFromAkkaAppsEventBus = new MsgFromAkkaAppsEventBus
private val msgToAkkaAppsEventBus = new MsgToAkkaAppsEventBus private val msgToRedisEventBus = new MsgToRedisEventBus
private val msgToClientEventBus = new MsgToClientEventBus private val msgToClientEventBus = new MsgToClientEventBus
private val redisPublisher = new RedisPublisher(system) private val redisPublisher = new RedisPublisher(system)
private val msgSender: MessageSender = new MessageSender(redisPublisher) private val msgSender: MessageSender = new MessageSender(redisPublisher)
private val messageSenderActorRef = system.actorOf(
MessageSenderActor.props(msgSender), "messageSenderActor")
jsonMsgToAkkaAppsBus.subscribe(messageSenderActorRef, toAkkaAppsJsonChannel)
private val meetingManagerActorRef = system.actorOf( private val meetingManagerActorRef = system.actorOf(
MeetingManagerActor.props(msgToAkkaAppsEventBus, msgToClientEventBus), "meetingManagerActor") MeetingManagerActor.props(msgToRedisEventBus, msgToClientEventBus), "meetingManagerActor")
msgFromAkkaAppsEventBus.subscribe(meetingManagerActorRef, fromAkkaAppsChannel) msgFromAkkaAppsEventBus.subscribe(meetingManagerActorRef, fromAkkaAppsChannel)
msgFromClientEventBus.subscribe(meetingManagerActorRef, fromClientChannel) msgFromClientEventBus.subscribe(meetingManagerActorRef, fromClientChannel)
private val receivedJsonMsgBus = new JsonMsgFromAkkaAppsBus private val receivedJsonMsgBus = new JsonMsgFromAkkaAppsBus
private val msgToAkkaAppsToJsonActor = system.actorOf( private val msgToRedisActor = system.actorOf(
MsgToAkkaAppsToJsonActor.props(jsonMsgToAkkaAppsBus), "msgToAkkaAppsToJsonActor") MsgToRedisActor.props(msgSender), "msgToRedisActor")
msgToAkkaAppsEventBus.subscribe(msgToAkkaAppsToJsonActor, toAkkaAppsChannel) msgToRedisEventBus.subscribe(msgToRedisActor, toRedisChannel)
private val msgToClientJsonActor = system.actorOf( private val msgToClientJsonActor = system.actorOf(
MsgToClientJsonActor.props(msgToClientGW), "msgToClientJsonActor") MsgToClientJsonActor.props(msgToClientGW), "msgToClientJsonActor")
@ -79,11 +70,6 @@ class ClientGWApplication(val msgToClientGW: MsgToClientGW) extends SystemConfig
msgFromClientEventBus.publish(MsgFromClientBusMsg(fromClientChannel, new MsgFromClientMsg(connInfo, json))) msgFromClientEventBus.publish(MsgFromClientBusMsg(fromClientChannel, new MsgFromClientMsg(connInfo, json)))
} }
def send(channel: String, json: String): Unit = {
//log.debug("Sending message {}", json)
jsonMsgToAkkaAppsBus.publish(JsonMsgToAkkaAppsBusMsg(toAkkaAppsJsonChannel, new JsonMsgToSendToAkkaApps(channel, json)))
}
def shutdown(): Unit = { def shutdown(): Unit = {
system.terminate() system.terminate()
} }

View File

@ -15,8 +15,4 @@ class ClientInGW(val clientGWApp: ClientGWApplication) extends IClientInGW {
def handleMsgFromClient(connInfo: ConnInfo, json: String): Unit = { def handleMsgFromClient(connInfo: ConnInfo, json: String): Unit = {
clientGWApp.handleMsgFromClient(connInfo, json) clientGWApp.handleMsgFromClient(connInfo, json)
} }
def send(channel: String, json: String): Unit = {
clientGWApp.send(channel, json)
}
} }

View File

@ -1,26 +0,0 @@
package org.bigbluebutton.client
import akka.actor.{Actor, ActorLogging, Props}
import org.bigbluebutton.client.bus.{JsonMsgToAkkaAppsBus, JsonMsgToAkkaAppsBusMsg, JsonMsgToSendToAkkaApps}
import org.bigbluebutton.common2.msgs.BbbCommonEnvJsNodeMsg
import org.bigbluebutton.common2.util.JsonUtil
object MsgToAkkaAppsToJsonActor {
def props(jsonMsgToAkkaAppsBus: JsonMsgToAkkaAppsBus): Props =
Props(classOf[MsgToAkkaAppsToJsonActor], jsonMsgToAkkaAppsBus)
}
class MsgToAkkaAppsToJsonActor(jsonMsgToAkkaAppsBus: JsonMsgToAkkaAppsBus)
extends Actor with ActorLogging with SystemConfiguration {
def receive = {
case msg: BbbCommonEnvJsNodeMsg => handle(msg)
}
def handle(msg: BbbCommonEnvJsNodeMsg): Unit = {
val json = JsonUtil.toJson(msg)
val jsonMsg = JsonMsgToSendToAkkaApps(toAkkaAppsRedisChannel, json)
jsonMsgToAkkaAppsBus.publish(JsonMsgToAkkaAppsBusMsg(toAkkaAppsJsonChannel, jsonMsg))
}
}

View File

@ -0,0 +1,30 @@
package org.bigbluebutton.client
import akka.actor.{Actor, ActorLogging, Props}
import org.bigbluebutton.common2.msgs.BbbCommonEnvJsNodeMsg
import org.bigbluebutton.common2.util.JsonUtil
import org.bigbluebutton.client.endpoint.redis.MessageSender
import org.bigbluebutton.common2.msgs.LookUpUserReqMsg
object MsgToRedisActor {
def props(msgSender: MessageSender): Props =
Props(classOf[MsgToRedisActor], msgSender)
}
class MsgToRedisActor(msgSender: MessageSender)
extends Actor with ActorLogging with SystemConfiguration {
def receive = {
case msg: BbbCommonEnvJsNodeMsg => handle(msg)
}
def handle(msg: BbbCommonEnvJsNodeMsg): Unit = {
val json = JsonUtil.toJson(msg)
msg.envelope.name match {
case LookUpUserReqMsg.NAME => msgSender.send(toThirdPartyRedisChannel, json)
case _ => msgSender.send(toAkkaAppsRedisChannel, json)
}
}
}

View File

@ -12,12 +12,12 @@ trait SystemConfiguration {
lazy val toAkkaAppsRedisChannel = Try(config.getString("redis.toAkkaAppsRedisChannel")).getOrElse("to-akka-apps-redis-channel") lazy val toAkkaAppsRedisChannel = Try(config.getString("redis.toAkkaAppsRedisChannel")).getOrElse("to-akka-apps-redis-channel")
lazy val fromAkkaAppsRedisChannel = Try(config.getString("redis.fromAkkaAppsRedisChannel")).getOrElse("from-akka-apps-redis-channel") lazy val fromAkkaAppsRedisChannel = Try(config.getString("redis.fromAkkaAppsRedisChannel")).getOrElse("from-akka-apps-redis-channel")
lazy val meetingManagerChannel = Try(config.getString("eventBus.meetingManagerChannel")).getOrElse("FOOOOOOOOO") lazy val toThirdPartyRedisChannel = Try(config.getString("redis.toThirdPartyRedisChannel")).getOrElse("to-third-party-redis-channel")
lazy val fromThirdPartyRedisChannel = Try(config.getString("redis.fromThirdPartyRedisChannel")).getOrElse("from-third-party-redis-channel")
lazy val fromAkkaAppsChannel = Try(config.getString("eventBus.fromAkkaAppsChannel")).getOrElse("from-akka-apps-channel") lazy val fromAkkaAppsChannel = Try(config.getString("eventBus.fromAkkaAppsChannel")).getOrElse("from-akka-apps-channel")
lazy val toAkkaAppsChannel = Try(config.getString("eventBus.toAkkaAppsChannel")).getOrElse("to-akka-apps-channel") lazy val toRedisChannel = Try(config.getString("eventBus.toRedisChannel")).getOrElse("to-redis-channel")
lazy val fromClientChannel = Try(config.getString("eventBus.fromClientChannel")).getOrElse("from-client-channel") lazy val fromClientChannel = Try(config.getString("eventBus.fromClientChannel")).getOrElse("from-client-channel")
lazy val toClientChannel = Try(config.getString("eventBus.toClientChannel")).getOrElse("to-client-channel") lazy val toClientChannel = Try(config.getString("eventBus.toClientChannel")).getOrElse("to-client-channel")
lazy val toAkkaAppsJsonChannel = Try(config.getString("eventBus.toAkkaAppsChannel")).getOrElse("to-akka-apps-json-channel")
lazy val fromAkkaAppsJsonChannel = Try(config.getString("eventBus.fromAkkaAppsChannel")).getOrElse("from-akka-apps-json-channel") lazy val fromAkkaAppsJsonChannel = Try(config.getString("eventBus.fromAkkaAppsChannel")).getOrElse("from-akka-apps-json-channel")
lazy val fromAkkaAppsWbRedisChannel = Try(config.getString("redis.fromAkkaAppsWbRedisChannel")).getOrElse("from-akka-apps-wb-redis-channel") lazy val fromAkkaAppsWbRedisChannel = Try(config.getString("redis.fromAkkaAppsWbRedisChannel")).getOrElse("from-akka-apps-wb-redis-channel")

View File

@ -1,31 +0,0 @@
package org.bigbluebutton.client.bus
import akka.actor.ActorRef
import akka.event.{EventBus, LookupClassification}
case class JsonMsgToSendToAkkaApps(channel: String, json: String)
case class JsonMsgToAkkaAppsBusMsg(val topic: String, payload: JsonMsgToSendToAkkaApps)
class JsonMsgToAkkaAppsBus extends EventBus with LookupClassification {
type Event = JsonMsgToAkkaAppsBusMsg
type Classifier = String
type Subscriber = ActorRef
// is used for extracting the classifier from the incoming events
override protected def classify(event: Event): Classifier = event.topic
// will be invoked for each event for all subscribers which registered themselves
// for the events classifier
override protected def publish(event: Event, subscriber: Subscriber): Unit = {
subscriber ! event.payload
}
// must define a full order over the subscribers, expressed as expected from
// `java.lang.Comparable.compare`
override protected def compareSubscribers(a: Subscriber, b: Subscriber): Int =
a.compareTo(b)
// determines the initial size of the index data structure
// used internally (i.e. the expected number of different classifiers)
override protected def mapSize: Int = 128
}

View File

@ -4,10 +4,10 @@ import akka.actor.ActorRef
import akka.event.{EventBus, LookupClassification} import akka.event.{EventBus, LookupClassification}
import org.bigbluebutton.common2.msgs.{BbbCommonEnvJsNodeMsg} import org.bigbluebutton.common2.msgs.{BbbCommonEnvJsNodeMsg}
case class MsgToAkkaApps(val topic: String, val payload: BbbCommonEnvJsNodeMsg) case class MsgToRedis(val topic: String, val payload: BbbCommonEnvJsNodeMsg)
class MsgToAkkaAppsEventBus extends EventBus with LookupClassification { class MsgToRedisEventBus extends EventBus with LookupClassification {
type Event = MsgToAkkaApps type Event = MsgToRedis
type Classifier = String type Classifier = String
type Subscriber = ActorRef type Subscriber = ActorRef

View File

@ -15,7 +15,7 @@ import redis.api.servers.ClientSetname
object AppsRedisSubscriberActor extends SystemConfiguration { object AppsRedisSubscriberActor extends SystemConfiguration {
val channels = Seq(fromAkkaAppsRedisChannel, fromAkkaAppsWbRedisChannel, fromAkkaAppsChatRedisChannel, fromAkkaAppsPresRedisChannel) val channels = Seq(fromAkkaAppsRedisChannel, fromAkkaAppsWbRedisChannel, fromAkkaAppsChatRedisChannel, fromAkkaAppsPresRedisChannel, fromThirdPartyRedisChannel)
val patterns = Seq("bigbluebutton:from-bbb-apps:*") val patterns = Seq("bigbluebutton:from-bbb-apps:*")
def props(jsonMsgBus: JsonMsgFromAkkaAppsBus): Props = def props(jsonMsgBus: JsonMsgFromAkkaAppsBus): Props =

View File

@ -34,6 +34,7 @@ object AllowedMessageNames {
GetGuestPolicyReqMsg.NAME, GetGuestPolicyReqMsg.NAME,
SetGuestPolicyCmdMsg.NAME, SetGuestPolicyCmdMsg.NAME,
GuestsWaitingApprovedMsg.NAME, GuestsWaitingApprovedMsg.NAME,
UserActivitySignCmdMsg.NAME,
// Webcams // Webcams
GetWebcamsOnlyForModeratorReqMsg.NAME, GetWebcamsOnlyForModeratorReqMsg.NAME,
@ -50,6 +51,7 @@ object AllowedMessageNames {
SendGroupChatMessageMsg.NAME, SendGroupChatMessageMsg.NAME,
ClearPublicChatHistoryPubMsg.NAME, ClearPublicChatHistoryPubMsg.NAME,
CreateGroupChatReqMsg.NAME, CreateGroupChatReqMsg.NAME,
UserTypingPubMsg.NAME,
// Presentation Messages // Presentation Messages
ResizeAndMovePagePubMsg.NAME, ResizeAndMovePagePubMsg.NAME,
@ -106,6 +108,9 @@ object AllowedMessageNames {
BreakoutRoomsListMsg.NAME, BreakoutRoomsListMsg.NAME,
// System // System
ClientToServerLatencyTracerMsg.NAME ClientToServerLatencyTracerMsg.NAME,
// Third-party Message
LookUpUserReqMsg.NAME
) )
} }

View File

@ -1,16 +1,16 @@
package org.bigbluebutton.client.meeting package org.bigbluebutton.client.meeting
import akka.actor.ActorContext import akka.actor.ActorContext
import org.bigbluebutton.client.bus.{MsgToAkkaAppsEventBus, MsgToClientEventBus} import org.bigbluebutton.client.bus.{MsgToRedisEventBus, MsgToClientEventBus}
object Meeting { object Meeting {
def apply(meetingId: String, msgToAkkaAppsEventBus: MsgToAkkaAppsEventBus, def apply(meetingId: String, msgToRedisEventBus: MsgToRedisEventBus,
msgToClientEventBus: MsgToClientEventBus)(implicit context: ActorContext) = msgToClientEventBus: MsgToClientEventBus)(implicit context: ActorContext) =
new Meeting(meetingId, msgToAkkaAppsEventBus, msgToClientEventBus)(context) new Meeting(meetingId, msgToRedisEventBus, msgToClientEventBus)(context)
} }
class Meeting(val meetingId: String, msgToAkkaAppsEventBus: MsgToAkkaAppsEventBus, class Meeting(val meetingId: String, msgToRedisEventBus: MsgToRedisEventBus,
msgToClientEventBus: MsgToClientEventBus)(implicit val context: ActorContext) { msgToClientEventBus: MsgToClientEventBus)(implicit val context: ActorContext) {
val actorRef = context.actorOf(MeetingActor.props(meetingId, msgToAkkaAppsEventBus, msgToClientEventBus), meetingId) val actorRef = context.actorOf(MeetingActor.props(meetingId, msgToRedisEventBus, msgToClientEventBus), meetingId)
} }

View File

@ -6,12 +6,12 @@ import org.bigbluebutton.client.bus._
import org.bigbluebutton.common2.msgs.{BbbCommonEnvJsNodeMsg, DisconnectAllClientsSysMsg, MessageTypes} import org.bigbluebutton.common2.msgs.{BbbCommonEnvJsNodeMsg, DisconnectAllClientsSysMsg, MessageTypes}
object MeetingActor { object MeetingActor {
def props(meetingId: String, msgToAkkaAppsEventBus: MsgToAkkaAppsEventBus, def props(meetingId: String, msgToRedisEventBus: MsgToRedisEventBus,
msgToClientEventBus: MsgToClientEventBus): Props = msgToClientEventBus: MsgToClientEventBus): Props =
Props(classOf[MeetingActor], meetingId, msgToAkkaAppsEventBus, msgToClientEventBus) Props(classOf[MeetingActor], meetingId, msgToRedisEventBus, msgToClientEventBus)
} }
class MeetingActor(val meetingId: String, msgToAkkaAppsEventBus: MsgToAkkaAppsEventBus, class MeetingActor(val meetingId: String, msgToRedisEventBus: MsgToRedisEventBus,
msgToClientEventBus: MsgToClientEventBus) msgToClientEventBus: MsgToClientEventBus)
extends Actor with ActorLogging extends Actor with ActorLogging
with SystemConfiguration{ with SystemConfiguration{
@ -27,7 +27,7 @@ class MeetingActor(val meetingId: String, msgToAkkaAppsEventBus: MsgToAkkaAppsEv
} }
private def createUser(id: String): User = { private def createUser(id: String): User = {
User(id, msgToAkkaAppsEventBus, meetingId, msgToClientEventBus) User(id, msgToRedisEventBus, meetingId, msgToClientEventBus)
} }
def handleConnectMsg(msg: ConnectMsg): Unit = { def handleConnectMsg(msg: ConnectMsg): Unit = {

View File

@ -6,12 +6,12 @@ import org.bigbluebutton.common2.msgs.{BbbCommonEnvJsNodeMsg, MessageTypes}
object MeetingManagerActor { object MeetingManagerActor {
def props(msgToAkkaAppsEventBus: MsgToAkkaAppsEventBus, def props(msgToAkkaAppsEventBus: MsgToRedisEventBus,
msgToClientEventBus: MsgToClientEventBus): Props = msgToClientEventBus: MsgToClientEventBus): Props =
Props(classOf[MeetingManagerActor], msgToAkkaAppsEventBus, msgToClientEventBus) Props(classOf[MeetingManagerActor], msgToAkkaAppsEventBus, msgToClientEventBus)
} }
class MeetingManagerActor(msgToAkkaAppsEventBus: MsgToAkkaAppsEventBus, class MeetingManagerActor(msgToRedisEventBus: MsgToRedisEventBus,
msgToClientEventBus: MsgToClientEventBus) msgToClientEventBus: MsgToClientEventBus)
extends Actor with ActorLogging { extends Actor with ActorLogging {
@ -26,7 +26,7 @@ class MeetingManagerActor(msgToAkkaAppsEventBus: MsgToAkkaAppsEventBus,
} }
def createMeeting(meetingId: String): Meeting = { def createMeeting(meetingId: String): Meeting = {
Meeting(meetingId, msgToAkkaAppsEventBus, msgToClientEventBus) Meeting(meetingId, msgToRedisEventBus, msgToClientEventBus)
} }
def handleConnectMsg(msg: ConnectMsg): Unit = { def handleConnectMsg(msg: ConnectMsg): Unit = {

View File

@ -1,21 +1,21 @@
package org.bigbluebutton.client.meeting package org.bigbluebutton.client.meeting
import akka.actor.ActorContext import akka.actor.ActorContext
import org.bigbluebutton.client.bus.{MsgToAkkaAppsEventBus, MsgToClientEventBus} import org.bigbluebutton.client.bus.{MsgToRedisEventBus, MsgToClientEventBus}
object User { object User {
def apply(userId: String, def apply(userId: String,
msgToAkkaAppsEventBus: MsgToAkkaAppsEventBus, msgToRedisEventBus: MsgToRedisEventBus,
meetingId: String, meetingId: String,
msgToClientEventBus: MsgToClientEventBus) (implicit context: ActorContext): User = msgToClientEventBus: MsgToClientEventBus) (implicit context: ActorContext): User =
new User(userId, msgToAkkaAppsEventBus, meetingId, msgToClientEventBus)(context) new User(userId, msgToRedisEventBus, meetingId, msgToClientEventBus)(context)
} }
class User(val userId: String, class User(val userId: String,
msgToAkkaAppsEventBus: MsgToAkkaAppsEventBus, msgToRedisEventBus: MsgToRedisEventBus,
meetingId: String, meetingId: String,
msgToClientEventBus: MsgToClientEventBus)(implicit val context: ActorContext) { msgToClientEventBus: MsgToClientEventBus)(implicit val context: ActorContext) {
val actorRef = context.actorOf(UserActor.props(userId, msgToAkkaAppsEventBus, val actorRef = context.actorOf(UserActor.props(userId, msgToRedisEventBus,
meetingId, msgToClientEventBus), meetingId + "-" + userId) meetingId, msgToClientEventBus), meetingId + "-" + userId)
} }

View File

@ -11,14 +11,14 @@ import scala.util.{Failure, Success}
object UserActor { object UserActor {
def props(userId: String, def props(userId: String,
msgToAkkaAppsEventBus: MsgToAkkaAppsEventBus, msgToRedisEventBus: MsgToRedisEventBus,
meetingId: String, meetingId: String,
msgToClientEventBus: MsgToClientEventBus): Props = msgToClientEventBus: MsgToClientEventBus): Props =
Props(classOf[UserActor], userId, msgToAkkaAppsEventBus, meetingId, msgToClientEventBus) Props(classOf[UserActor], userId, msgToRedisEventBus, meetingId, msgToClientEventBus)
} }
class UserActor(val userId: String, class UserActor(val userId: String,
msgToAkkaAppsEventBus: MsgToAkkaAppsEventBus, msgToRedisEventBus: MsgToRedisEventBus,
meetingId: String, meetingId: String,
msgToClientEventBus: MsgToClientEventBus) msgToClientEventBus: MsgToClientEventBus)
extends Actor with ActorLogging with SystemConfiguration { extends Actor with ActorLogging with SystemConfiguration {
@ -133,8 +133,8 @@ class UserActor(val userId: String,
for { for {
jsonNode <- convertToJsonNode(json) jsonNode <- convertToJsonNode(json)
} yield { } yield {
val akkaMsg = BbbCommonEnvJsNodeMsg(envelope, jsonNode) val jsNodeMsg = BbbCommonEnvJsNodeMsg(envelope, jsonNode)
msgToAkkaAppsEventBus.publish(MsgToAkkaApps(toAkkaAppsChannel, akkaMsg)) msgToRedisEventBus.publish(MsgToRedis(toRedisChannel, jsNodeMsg))
} }
} }
case None => case None =>

View File

@ -1,22 +1,22 @@
/** /**
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/ * BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
* *
* Copyright (c) 2014 BigBlueButton Inc. and by respective authors (see below). * Copyright (c) 2014 BigBlueButton Inc. and by respective authors (see below).
* *
* This program is free software; you can redistribute it and/or modify it under the * 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 * 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 * Foundation; either version 3.0 of the License, or (at your option) any later
* version. * version.
* *
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY * 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 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. * 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 * You should have received a copy of the GNU Lesser General Public License along
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>. * with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
* *
*/ */
package org.bigbluebutton.clientcheck.service package org.bigbluebutton.clientcheck.service
{ {
import flash.events.AsyncErrorEvent; import flash.events.AsyncErrorEvent;
@ -43,6 +43,7 @@ package org.bigbluebutton.clientcheck.service
private static var RECORD_MOCK:Boolean=false; private static var RECORD_MOCK:Boolean=false;
private static var EXTERNAL_USER_ID_MOCK:String="123456"; private static var EXTERNAL_USER_ID_MOCK:String="123456";
private static var INTERNAL_USER_ID_MOCK:String="654321"; private static var INTERNAL_USER_ID_MOCK:String="654321";
private static var CLIENT_CONN_ID:String="client-conn-id";
private static var LOCK_ON_MOCK:Boolean=true; private static var LOCK_ON_MOCK:Boolean=true;
public function init():void public function init():void
@ -60,7 +61,7 @@ package org.bigbluebutton.clientcheck.service
// sip has a different way of connecting to the red5 server, need to fake connection data. // sip has a different way of connecting to the red5 server, need to fake connection data.
if (systemConfiguration.rtmpApps[i].applicationUri.indexOf("sip") > 0) if (systemConfiguration.rtmpApps[i].applicationUri.indexOf("sip") > 0)
{ {
_netConnection.connect(systemConfiguration.rtmpApps[i].applicationUri, ROOM_MOCK, EXTERNAL_USER_ID_MOCK, USER_NAME_MOCK); _netConnection.connect(systemConfiguration.rtmpApps[i].applicationUri, ROOM_MOCK, EXTERNAL_USER_ID_MOCK, USER_NAME_MOCK, INTERNAL_USER_ID_MOCK, CLIENT_CONN_ID);
continue; continue;
} }
else else

View File

@ -0,0 +1,13 @@
FROM sbt:0.13.8
ARG COMMON_VERSION
COPY . /bbb-common-message
RUN cd /bbb-common-message \
&& sed -i "s|\(version := \)\".*|\1\"$COMMON_VERSION\"|g" build.sbt \
&& echo 'publishTo := Some(Resolver.file("file", new File(Path.userHome.absolutePath+"/.m2/repository")))' | tee -a build.sbt \
&& sbt compile \
&& sbt publish \
&& sbt publishLocal

View File

@ -2,13 +2,14 @@ package org.bigbluebutton.common2.domain
case class ConfigProps(defaultConfigToken: String, config: String) case class ConfigProps(defaultConfigToken: String, config: String)
case class DurationProps(duration: Int, createdTime: Long, createdDate: String, maxInactivityTimeoutMinutes: Int, case class DurationProps(duration: Int, createdTime: Long, createdDate: String,
warnMinutesBeforeMax: Int, meetingExpireIfNoUserJoinedInMinutes: Int, maxInactivityTimeoutMinutes: Int, warnMinutesBeforeMax: Int,
meetingExpireWhenLastUserLeftInMinutes: Int) meetingExpireIfNoUserJoinedInMinutes: Int, meetingExpireWhenLastUserLeftInMinutes: Int,
userInactivityInspectTimerInMinutes: Int, userInactivityThresholdInMinutes: Int, userActivitySignResponseDelayInMinutes: Int)
case class MeetingProp(name: String, extId: String, intId: String, isBreakout: Boolean) case class MeetingProp(name: String, extId: String, intId: String, isBreakout: Boolean)
case class BreakoutProps(parentId: String, sequence: Int, breakoutRooms: Vector[String]) case class BreakoutProps(parentId: String, sequence: Int, freeJoin: Boolean, breakoutRooms: Vector[String])
case class PasswordProp(moderatorPass: String, viewerPass: String) case class PasswordProp(moderatorPass: String, viewerPass: String)

View File

@ -8,13 +8,13 @@ package org.bigbluebutton.common2.msgs
object BreakoutRoomJoinURLEvtMsg { val NAME = "BreakoutRoomJoinURLEvtMsg" } object BreakoutRoomJoinURLEvtMsg { val NAME = "BreakoutRoomJoinURLEvtMsg" }
case class BreakoutRoomJoinURLEvtMsg(header: BbbClientMsgHeader, body: BreakoutRoomJoinURLEvtMsgBody) extends BbbCoreMsg case class BreakoutRoomJoinURLEvtMsg(header: BbbClientMsgHeader, body: BreakoutRoomJoinURLEvtMsgBody) extends BbbCoreMsg
case class BreakoutRoomJoinURLEvtMsgBody(parentId: String, breakoutId: String, externalId: String, case class BreakoutRoomJoinURLEvtMsgBody(parentId: String, breakoutId: String, externalId: String,
userId: String, redirectJoinURL: String, noRedirectJoinURL: String) userId: String, redirectJoinURL: String, redirectToHtml5JoinURL: String)
// Outgoing messages // Outgoing messages
object BreakoutRoomsListEvtMsg { val NAME = "BreakoutRoomsListEvtMsg" } object BreakoutRoomsListEvtMsg { val NAME = "BreakoutRoomsListEvtMsg" }
case class BreakoutRoomsListEvtMsg(header: BbbClientMsgHeader, body: BreakoutRoomsListEvtMsgBody) extends BbbCoreMsg case class BreakoutRoomsListEvtMsg(header: BbbClientMsgHeader, body: BreakoutRoomsListEvtMsgBody) extends BbbCoreMsg
case class BreakoutRoomsListEvtMsgBody(meetingId: String, rooms: Vector[BreakoutRoomInfo], roomsReady: Boolean) case class BreakoutRoomsListEvtMsgBody(meetingId: String, rooms: Vector[BreakoutRoomInfo], roomsReady: Boolean)
case class BreakoutRoomInfo(name: String, externalId: String, breakoutId: String, sequence: Int) case class BreakoutRoomInfo(name: String, externalId: String, breakoutId: String, sequence: Int, freeJoin:Boolean)
object BreakoutRoomsListMsg { val NAME = "BreakoutRoomsListMsg" } object BreakoutRoomsListMsg { val NAME = "BreakoutRoomsListMsg" }
case class BreakoutRoomsListMsg(header: BbbClientMsgHeader, body: BreakoutRoomsListMsgBody) extends StandardMsg case class BreakoutRoomsListMsg(header: BbbClientMsgHeader, body: BreakoutRoomsListMsgBody) extends StandardMsg
@ -39,7 +39,7 @@ package org.bigbluebutton.common2.msgs
case class CreateBreakoutRoomSysCmdMsg(header: BbbCoreBaseHeader, case class CreateBreakoutRoomSysCmdMsg(header: BbbCoreBaseHeader,
body: CreateBreakoutRoomSysCmdMsgBody) extends BbbCoreMsg body: CreateBreakoutRoomSysCmdMsgBody) extends BbbCoreMsg
case class CreateBreakoutRoomSysCmdMsgBody(meetingId: String, room: BreakoutRoomDetail) case class CreateBreakoutRoomSysCmdMsgBody(meetingId: String, room: BreakoutRoomDetail)
case class BreakoutRoomDetail(breakoutMeetingId: String, name: String, parentId: String, sequence: Integer, case class BreakoutRoomDetail(breakoutMeetingId: String, name: String, parentId: String, sequence: Integer, freeJoin:Boolean,
voiceConfId: String, durationInMinutes: Int, moderatorPassword: String, viewerPassword: String, voiceConfId: String, durationInMinutes: Int, moderatorPassword: String, viewerPassword: String,
sourcePresentationId: String, sourcePresentationSlide: Int, record: Boolean) sourcePresentationId: String, sourcePresentationSlide: Int, record: Boolean)
@ -49,7 +49,7 @@ package org.bigbluebutton.common2.msgs
object CreateBreakoutRoomsCmdMsg { val NAME = "CreateBreakoutRoomsCmdMsg" } object CreateBreakoutRoomsCmdMsg { val NAME = "CreateBreakoutRoomsCmdMsg" }
case class CreateBreakoutRoomsCmdMsg(header: BbbClientMsgHeader, body: CreateBreakoutRoomsCmdMsgBody) extends StandardMsg case class CreateBreakoutRoomsCmdMsg(header: BbbClientMsgHeader, body: CreateBreakoutRoomsCmdMsgBody) extends StandardMsg
case class CreateBreakoutRoomsCmdMsgBody(meetingId: String, durationInMinutes: Int, record: Boolean, rooms: Vector[BreakoutRoomMsgBody]) case class CreateBreakoutRoomsCmdMsgBody(meetingId: String, durationInMinutes: Int, record: Boolean, rooms: Vector[BreakoutRoomMsgBody])
case class BreakoutRoomMsgBody(name: String, sequence: Int, users: Vector[String]) case class BreakoutRoomMsgBody(name: String, sequence: Int, freeJoin:Boolean, users: Vector[String])
// Sent by user to request ending all the breakout rooms // Sent by user to request ending all the breakout rooms
object EndAllBreakoutRoomsMsg { val NAME = "EndAllBreakoutRoomsMsg" } object EndAllBreakoutRoomsMsg { val NAME = "EndAllBreakoutRoomsMsg" }
@ -69,7 +69,7 @@ package org.bigbluebutton.common2.msgs
object RequestBreakoutJoinURLRespMsg { val NAME = "RequestBreakoutJoinURLRespMsg" } object RequestBreakoutJoinURLRespMsg { val NAME = "RequestBreakoutJoinURLRespMsg" }
case class RequestBreakoutJoinURLRespMsg(header: BbbClientMsgHeader, body: RequestBreakoutJoinURLRespMsgBody) extends BbbCoreMsg case class RequestBreakoutJoinURLRespMsg(header: BbbClientMsgHeader, body: RequestBreakoutJoinURLRespMsgBody) extends BbbCoreMsg
case class RequestBreakoutJoinURLRespMsgBody(parentId: String, breakoutId: String, case class RequestBreakoutJoinURLRespMsgBody(parentId: String, breakoutId: String,
userId: String, redirectJoinURL: String, noRedirectJoinURL: String) userId: String, redirectJoinURL: String, redirectToHtml5JoinURL: String)
object TransferUserToMeetingEvtMsg { val NAME = "TransferUserToMeetingEvtMsg" } object TransferUserToMeetingEvtMsg { val NAME = "TransferUserToMeetingEvtMsg" }
@ -89,7 +89,7 @@ case class RequestBreakoutJoinURLRespMsgBody(parentId: String, breakoutId: Strin
case class BreakoutUserVO(id: String, name: String) case class BreakoutUserVO(id: String, name: String)
case class BreakoutRoomVO(id: String, externalId: String, name: String, parentId: String, case class BreakoutRoomVO(id: String, externalId: String, name: String, parentId: String,
sequence: Int, voiceConf: String, sequence: Int, freeJoin: Boolean, voiceConf: String,
assignedUsers: Vector[String], users: Vector[BreakoutUserVO]) assignedUsers: Vector[String], users: Vector[BreakoutUserVO])

View File

@ -1,39 +1,42 @@
package org.bigbluebutton.common2.msgs package org.bigbluebutton.common2.msgs
/* In Messages */ /* In Messages */
object GetChatHistoryReqMsg { val NAME = "GetChatHistoryReqMsg"} object GetChatHistoryReqMsg { val NAME = "GetChatHistoryReqMsg" }
case class GetChatHistoryReqMsg(header: BbbClientMsgHeader, body: GetChatHistoryReqMsgBody) extends StandardMsg case class GetChatHistoryReqMsg(header: BbbClientMsgHeader, body: GetChatHistoryReqMsgBody) extends StandardMsg
case class GetChatHistoryReqMsgBody() case class GetChatHistoryReqMsgBody()
object SendPublicMessagePubMsg { val NAME = "SendPublicMessagePubMsg"} object SendPublicMessagePubMsg { val NAME = "SendPublicMessagePubMsg" }
case class SendPublicMessagePubMsg(header: BbbClientMsgHeader, body: SendPublicMessagePubMsgBody) extends StandardMsg case class SendPublicMessagePubMsg(header: BbbClientMsgHeader, body: SendPublicMessagePubMsgBody) extends StandardMsg
case class SendPublicMessagePubMsgBody(message: ChatMessageVO) case class SendPublicMessagePubMsgBody(message: ChatMessageVO)
object SendPrivateMessagePubMsg { val NAME = "SendPrivateMessagePubMsg"} object SendPrivateMessagePubMsg { val NAME = "SendPrivateMessagePubMsg" }
case class SendPrivateMessagePubMsg(header: BbbClientMsgHeader, body: SendPrivateMessagePubMsgBody) extends StandardMsg case class SendPrivateMessagePubMsg(header: BbbClientMsgHeader, body: SendPrivateMessagePubMsgBody) extends StandardMsg
case class SendPrivateMessagePubMsgBody(message: ChatMessageVO) case class SendPrivateMessagePubMsgBody(message: ChatMessageVO)
object ClearPublicChatHistoryPubMsg { val NAME = "ClearPublicChatHistoryPubMsg"} object ClearPublicChatHistoryPubMsg { val NAME = "ClearPublicChatHistoryPubMsg" }
case class ClearPublicChatHistoryPubMsg(header: BbbClientMsgHeader, body: ClearPublicChatHistoryPubMsgBody) extends StandardMsg case class ClearPublicChatHistoryPubMsg(header: BbbClientMsgHeader, body: ClearPublicChatHistoryPubMsgBody) extends StandardMsg
case class ClearPublicChatHistoryPubMsgBody(chatId: String) case class ClearPublicChatHistoryPubMsgBody(chatId: String)
/* Out Messages */ /* Out Messages */
object GetChatHistoryRespMsg { val NAME = "GetChatHistoryRespMsg"} object GetChatHistoryRespMsg { val NAME = "GetChatHistoryRespMsg" }
case class GetChatHistoryRespMsg(header: BbbClientMsgHeader, body: GetChatHistoryRespMsgBody) extends StandardMsg case class GetChatHistoryRespMsg(header: BbbClientMsgHeader, body: GetChatHistoryRespMsgBody) extends StandardMsg
case class GetChatHistoryRespMsgBody(history: Array[ChatMessageVO]) case class GetChatHistoryRespMsgBody(history: Array[ChatMessageVO])
object SendPublicMessageEvtMsg { val NAME = "SendPublicMessageEvtMsg"} object SendPublicMessageEvtMsg { val NAME = "SendPublicMessageEvtMsg" }
case class SendPublicMessageEvtMsg(header: BbbClientMsgHeader, body: SendPublicMessageEvtMsgBody) extends StandardMsg case class SendPublicMessageEvtMsg(header: BbbClientMsgHeader, body: SendPublicMessageEvtMsgBody) extends StandardMsg
case class SendPublicMessageEvtMsgBody(message: ChatMessageVO) case class SendPublicMessageEvtMsgBody(message: ChatMessageVO)
object SendPrivateMessageEvtMsg { val NAME = "SendPrivateMessageEvtMsg"} object SendPrivateMessageEvtMsg { val NAME = "SendPrivateMessageEvtMsg" }
case class SendPrivateMessageEvtMsg(header: BbbClientMsgHeader, body: SendPrivateMessageEvtMsgBody) extends StandardMsg case class SendPrivateMessageEvtMsg(header: BbbClientMsgHeader, body: SendPrivateMessageEvtMsgBody) extends StandardMsg
case class SendPrivateMessageEvtMsgBody(message: ChatMessageVO) case class SendPrivateMessageEvtMsgBody(message: ChatMessageVO)
object ClearPublicChatHistoryEvtMsg { val NAME = "ClearPublicChatHistoryEvtMsg"} object ClearPublicChatHistoryEvtMsg { val NAME = "ClearPublicChatHistoryEvtMsg" }
case class ClearPublicChatHistoryEvtMsg(header: BbbClientMsgHeader, body: ClearPublicChatHistoryEvtMsgBody) extends StandardMsg case class ClearPublicChatHistoryEvtMsg(header: BbbClientMsgHeader, body: ClearPublicChatHistoryEvtMsgBody) extends StandardMsg
case class ClearPublicChatHistoryEvtMsgBody(chatId: String) case class ClearPublicChatHistoryEvtMsgBody(chatId: String)
object UserTypingEvtMsg { val NAME = "UserTypingEvtMsg" }
case class UserTypingEvtMsg(header: BbbClientMsgHeader, body: UserTypingEvtMsgBody) extends StandardMsg
case class UserTypingEvtMsgBody(chatId: String, userId: String)
case class ChatMessageVO(fromUserId: String, fromUsername: String, fromColor: String, fromTime: Long, fromTimezoneOffset: Int, case class ChatMessageVO(fromUserId: String, fromUsername: String, fromColor: String, fromTime: Long, fromTimezoneOffset: Int,
toUserId: String, toUsername: String, message: String) toUserId: String, toUsername: String, message: String)

Some files were not shown because too many files have changed in this diff Show More