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:
commit
845ffc8343
2
akka-bbb-apps/.dockerignore
Normal file
2
akka-bbb-apps/.dockerignore
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
Dockerfile
|
||||||
|
|
24
akka-bbb-apps/Dockerfile
Normal file
24
akka-bbb-apps/Dockerfile
Normal 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"]
|
@ -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
|
||||||
}
|
}
|
@ -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
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -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>
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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] = {
|
||||||
|
@ -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] = {
|
||||||
|
@ -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)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
@ -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,
|
||||||
|
@ -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 {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
@ -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 {
|
||||||
|
4
akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/groupchats/GroupChatHdlrs.scala
Executable file → Normal file
4
akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/groupchats/GroupChatHdlrs.scala
Executable file → Normal 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)
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
@ -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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
@ -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)
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
@ -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 = {
|
||||||
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
20
akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/presentationpod/SetPresentationDownloadablePubMsgHdlr.scala
Normal file → Executable file
20
akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/presentationpod/SetPresentationDownloadablePubMsgHdlr.scala
Normal file → Executable 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)
|
||||||
|
@ -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)
|
||||||
|
@ -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")
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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)
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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()
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
@ -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 =>
|
||||||
}
|
}
|
||||||
|
@ -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],
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -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"
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
|
@ -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 {
|
||||||
|
@ -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"
|
||||||
}
|
}
|
||||||
|
@ -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 =>
|
||||||
|
@ -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"
|
||||||
|
}
|
@ -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 = {
|
||||||
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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 =>
|
||||||
|
@ -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])
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
@ -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")
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
26
akka-bbb-fsesl/Dockerfile
Normal 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"]
|
@ -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
177
akka-bbb-fsesl/wait-for-it.sh
Executable 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
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -4,6 +4,7 @@ akka {
|
|||||||
receive = on
|
receive = on
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
loggers = ["akka.event.slf4j.Slf4jLogger"]
|
||||||
loglevel = INFO
|
loglevel = INFO
|
||||||
stdout-loglevel = "INFO"
|
stdout-loglevel = "INFO"
|
||||||
|
|
||||||
|
@ -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>
|
||||||
|
|
||||||
|
@ -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 + "]")
|
||||||
|
@ -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
|
|||||||
}
|
}
|
||||||
|
|
||||||
%>
|
%>
|
||||||
|
|
||||||
|
@ -70,7 +70,7 @@ if (request.getParameterMap().isEmpty()) {
|
|||||||
<td> </td>
|
<td> </td>
|
||||||
<td style="text-align: right; ">Moderator Role:</td>
|
<td style="text-align: right; ">Moderator Role:</td>
|
||||||
<td style="width: 5px; "> </td>
|
<td style="width: 5px; "> </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://")) {
|
||||||
%>
|
%>
|
||||||
|
115
bbb-api-demo/src/main/webapp/demoHTML5Video.jsp
Normal file
115
bbb-api-demo/src/main/webapp/demoHTML5Video.jsp
Normal 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> </td>
|
||||||
|
<td style="text-align: right; ">Full Name:</td>
|
||||||
|
<td style="width: 5px; "> </td>
|
||||||
|
<td style="text-align: left "><input type="text" autofocus required name="username" /></td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td> </td>
|
||||||
|
<td> </td>
|
||||||
|
<td> </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>
|
@ -20,3 +20,5 @@
|
|||||||
|
|
||||||
<a href="demoHTML5.jsp">HTML5 Client Demo</a>
|
<a href="demoHTML5.jsp">HTML5 Client Demo</a>
|
||||||
|
|
||||||
|
<a href="demoHTML5Video.jsp">HTML5 Client Video Chat</a>
|
||||||
|
|
||||||
|
@ -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">
|
||||||
|
1
bbb-apps-common/src/main/java/org/bigbluebutton/client/IClientInGW.java
Executable file → Normal file
1
bbb-apps-common/src/main/java/org/bigbluebutton/client/IClientInGW.java
Executable file → Normal 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
24
bbb-apps-common/src/main/scala/org/bigbluebutton/client/ClientGWApplication.scala
Executable file → Normal file
24
bbb-apps-common/src/main/scala/org/bigbluebutton/client/ClientGWApplication.scala
Executable file → Normal 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()
|
||||||
}
|
}
|
||||||
|
4
bbb-apps-common/src/main/scala/org/bigbluebutton/client/ClientInGW.scala
Executable file → Normal file
4
bbb-apps-common/src/main/scala/org/bigbluebutton/client/ClientInGW.scala
Executable file → Normal 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)
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -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))
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
6
bbb-apps-common/src/main/scala/org/bigbluebutton/client/SystemConfiguration.scala
Executable file → Normal file
6
bbb-apps-common/src/main/scala/org/bigbluebutton/client/SystemConfiguration.scala
Executable file → Normal 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")
|
||||||
|
@ -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 event’s 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
|
|
||||||
}
|
|
@ -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
|
||||||
|
|
2
bbb-apps-common/src/main/scala/org/bigbluebutton/client/endpoint/redis/AppsRedisSubscriberActor.scala
Executable file → Normal file
2
bbb-apps-common/src/main/scala/org/bigbluebutton/client/endpoint/redis/AppsRedisSubscriberActor.scala
Executable file → Normal 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 =
|
||||||
|
7
bbb-apps-common/src/main/scala/org/bigbluebutton/client/meeting/AllowedMessageNames.scala
Executable file → Normal file
7
bbb-apps-common/src/main/scala/org/bigbluebutton/client/meeting/AllowedMessageNames.scala
Executable file → Normal 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
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
10
bbb-apps-common/src/main/scala/org/bigbluebutton/client/meeting/Meeting.scala
Executable file → Normal file
10
bbb-apps-common/src/main/scala/org/bigbluebutton/client/meeting/Meeting.scala
Executable file → Normal 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)
|
||||||
}
|
}
|
||||||
|
8
bbb-apps-common/src/main/scala/org/bigbluebutton/client/meeting/MeetingActor.scala
Executable file → Normal file
8
bbb-apps-common/src/main/scala/org/bigbluebutton/client/meeting/MeetingActor.scala
Executable file → Normal 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 = {
|
||||||
|
6
bbb-apps-common/src/main/scala/org/bigbluebutton/client/meeting/MeetingManagerActor.scala
Executable file → Normal file
6
bbb-apps-common/src/main/scala/org/bigbluebutton/client/meeting/MeetingManagerActor.scala
Executable file → Normal 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 = {
|
||||||
|
10
bbb-apps-common/src/main/scala/org/bigbluebutton/client/meeting/User.scala
Executable file → Normal file
10
bbb-apps-common/src/main/scala/org/bigbluebutton/client/meeting/User.scala
Executable file → Normal 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)
|
||||||
}
|
}
|
||||||
|
10
bbb-apps-common/src/main/scala/org/bigbluebutton/client/meeting/UserActor.scala
Executable file → Normal file
10
bbb-apps-common/src/main/scala/org/bigbluebutton/client/meeting/UserActor.scala
Executable file → Normal 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 =>
|
||||||
|
@ -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
|
||||||
|
13
bbb-common-message/Dockerfile
Normal file
13
bbb-common-message/Dockerfile
Normal 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
|
||||||
|
|
@ -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)
|
||||||
|
|
||||||
|
@ -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])
|
||||||
|
|
||||||
|
|
||||||
|
@ -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
Loading…
Reference in New Issue
Block a user