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 org.bigbluebutton.messages.BreakoutRoomJoinURL
|
||||
|
||||
|
||||
object BreakoutRoom {
|
||||
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
|
||||
@ -16,8 +16,8 @@ object BreakoutRoom {
|
||||
//| nal.bind.TypeAdapters$11@3ac42916], Factory[type=java.lang.Double+double,ada
|
||||
//| pter=com.google.gson.Gso
|
||||
//| 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"
|
||||
//| :"4455e780b6f62cd5fcf09367aef62d9bc5108375-1479728671031","noRedirectJoinUrl
|
||||
//| ":"http://bbb.riadvice.com/bigbluebutton/api/join?fullName=Opera&isBreakout=
|
||||
@ -26,14 +26,14 @@ object BreakoutRoom {
|
||||
//| 1521bca604e784ab23","parentMeetingId":"183f0bf3a0982a127bdb8161e0c44eb696b3e
|
||||
//| 75c-1479728593178","userId":"6pt0vfeaxdze_1"},"header":{"name":"BreakoutRoom
|
||||
//| JoinURL","version":"0.0.1","current_time":1479728673586,"timestamp":8549632}
|
||||
//| }
|
||||
//| }
|
||||
val brjum: BreakoutRoomJoinURL = gson.fromJson(string, classOf[BreakoutRoomJoinURL])
|
||||
//> brjum : org.bigbluebutton.messages.BreakoutRoomJoinURL = org.bigbluebutton.
|
||||
//| messages.BreakoutRoomJoinURL@3327bd23
|
||||
|
||||
println(brjum.payload.userId) //> 6pt0vfeaxdze_1
|
||||
println(brjum.payload.parentMeetingId) //> 183f0bf3a0982a127bdb8161e0c44eb696b3e75c-1479728593178
|
||||
println(brjum.payload.breakoutMeetingId) //> 4455e780b6f62cd5fcf09367aef62d9bc5108375-1479728671031
|
||||
println(brjum.payload.redirectJoinURL) //> null
|
||||
println(brjum.payload.noRedirectJoinURL) //> null
|
||||
//| messages.BreakoutRoomJoinURL@3327bd23
|
||||
|
||||
println(brjum.payload.userId) //> 6pt0vfeaxdze_1
|
||||
println(brjum.payload.parentMeetingId) //> 183f0bf3a0982a127bdb8161e0c44eb696b3e75c-1479728593178
|
||||
println(brjum.payload.breakoutMeetingId) //> 4455e780b6f62cd5fcf09367aef62d9bc5108375-1479728671031
|
||||
println(brjum.payload.redirectJoinURL) //> null
|
||||
println(brjum.payload.redirectToHtml5JoinURL) //> null
|
||||
}
|
@ -35,13 +35,6 @@ redis {
|
||||
keyExpiry=1209600
|
||||
}
|
||||
|
||||
inactivity {
|
||||
# time in seconds
|
||||
deadline=7200
|
||||
# inactivity warning message
|
||||
timeLeft=300
|
||||
}
|
||||
|
||||
expire {
|
||||
# time in seconds
|
||||
lastUserLeft = 60
|
||||
@ -82,6 +75,8 @@ services {
|
||||
|
||||
apps {
|
||||
checkPermissions = true
|
||||
endMeetingWhenNoMoreAuthedUsers = false
|
||||
endMeetingWhenNoMoreAuthedUsersAfterMinutes = 2
|
||||
}
|
||||
|
||||
voiceConf {
|
||||
@ -90,4 +85,9 @@ voiceConf {
|
||||
|
||||
recording {
|
||||
chapterBreakLengthInMinutes = 180
|
||||
}
|
||||
}
|
||||
|
||||
whiteboard {
|
||||
multiUserDefault = false
|
||||
}
|
||||
|
||||
|
@ -1,20 +1,20 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration>
|
||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>%date{ISO8601} %-5level %logger{36} - %msg%n</pattern>
|
||||
</encoder>
|
||||
<layout class="ch.qos.logback.classic.PatternLayout">
|
||||
<Pattern>%d{"yyyy-MM-dd'T'HH:mm:ss.SSSXXX"} %-5level %logger{35} - %msg%n</Pattern>
|
||||
</layout>
|
||||
</appender>
|
||||
|
||||
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<File>logs/bbb-apps-akka.log</File>
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<FileNamePattern>logs/bbb-apps-akka.%d{yyyy-MM-dd}.log</FileNamePattern>
|
||||
<!-- keep 30 days worth of history -->
|
||||
<MaxHistory>5</MaxHistory>
|
||||
<!-- keep 14 days worth of history -->
|
||||
<MaxHistory>14</MaxHistory>
|
||||
</rollingPolicy>
|
||||
<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>
|
||||
</appender>
|
||||
|
||||
@ -25,4 +25,4 @@
|
||||
<appender-ref ref="STDOUT"/>
|
||||
<appender-ref ref="FILE" />
|
||||
</root>
|
||||
</configuration>
|
||||
</configuration>
|
||||
|
@ -21,9 +21,6 @@ trait SystemConfiguration {
|
||||
lazy val red5DeskShareIP = Try(config.getString("red5.deskshareip")).getOrElse("127.0.0.1")
|
||||
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 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 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 SendRecordingTimerInternalMsg(meetingId: String) extends InMessage
|
||||
|
||||
case class ExtendMeetingDuration(meetingId: String, userId: String) extends InMessage
|
||||
case class DestroyMeetingInternalMsg(meetingId: String) extends InMessage
|
||||
|
||||
|
@ -9,10 +9,11 @@ object BreakoutModel {
|
||||
externalId: String,
|
||||
name: String,
|
||||
sequence: Integer,
|
||||
freeJoin: Boolean,
|
||||
voiceConf: String,
|
||||
assignedUsers: Vector[String]
|
||||
): 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 org.bigbluebutton.common2.msgs.AnnotationVO
|
||||
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 def saveWhiteboard(wb: Whiteboard) {
|
||||
@ -24,7 +25,7 @@ class WhiteboardModel {
|
||||
}
|
||||
|
||||
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] = {
|
||||
|
@ -56,10 +56,11 @@ object BreakoutRoomsUtil {
|
||||
"userID" -> urlEncode(userId),
|
||||
"isBreakout" -> urlEncode(isBreakout.toString()),
|
||||
"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] = {
|
||||
|
@ -17,37 +17,37 @@ trait BreakoutHdlrHelpers extends SystemConfiguration {
|
||||
for {
|
||||
user <- Users2x.findWithIntId(liveMeeting.users2x, userId)
|
||||
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)
|
||||
// We generate a first url with redirect -> true
|
||||
redirectBaseString = BreakoutRoomsUtil.createBaseString(redirectParams)
|
||||
redirectJoinURL = BreakoutRoomsUtil.createJoinURL(bbbWebAPI, apiCall, redirectBaseString,
|
||||
BreakoutRoomsUtil.calculateChecksum(apiCall, redirectBaseString, bbbWebSharedSecret))
|
||||
// We generate a second url with redirect -> false
|
||||
noRedirectBaseString = BreakoutRoomsUtil.createBaseString(noRedirectParams)
|
||||
noRedirectJoinURL = BreakoutRoomsUtil.createJoinURL(bbbWebAPI, apiCall, noRedirectBaseString,
|
||||
BreakoutRoomsUtil.calculateChecksum(apiCall, noRedirectBaseString, bbbWebSharedSecret))
|
||||
// We generate a second url with redirect -> true and joinViaHtml5 -> true
|
||||
redirectToHtml5BaseString = BreakoutRoomsUtil.createBaseString(redirectToHtml5Params)
|
||||
redirectToHtml5JoinURL = BreakoutRoomsUtil.createJoinURL(bbbWebAPI, apiCall, redirectToHtml5BaseString,
|
||||
BreakoutRoomsUtil.calculateChecksum(apiCall, redirectToHtml5BaseString, bbbWebSharedSecret))
|
||||
} yield {
|
||||
sendJoinURLMsg(liveMeeting.props.meetingProp.intId, breakoutId, externalMeetingId,
|
||||
userId, redirectJoinURL, noRedirectJoinURL)
|
||||
userId, redirectJoinURL, redirectToHtml5JoinURL)
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
userId: String, redirectJoinURL: String, noRedirectJoinURL: String): BbbCommonEnvCoreMsg = {
|
||||
userId: String, redirectJoinURL: String, redirectToHtml5JoinURL: String): BbbCommonEnvCoreMsg = {
|
||||
val routing = Routing.addMsgToClientRouting(MessageTypes.DIRECT, meetingId, userId)
|
||||
val envelope = BbbCoreEnvelope(BreakoutRoomJoinURLEvtMsg.NAME, routing)
|
||||
val header = BbbClientMsgHeader(BreakoutRoomJoinURLEvtMsg.NAME, meetingId, userId)
|
||||
|
||||
val body = BreakoutRoomJoinURLEvtMsgBody(meetingId, breakoutId, externalId,
|
||||
userId, redirectJoinURL, noRedirectJoinURL)
|
||||
userId, redirectJoinURL, redirectToHtml5JoinURL)
|
||||
val event = BreakoutRoomJoinURLEvtMsg(header, body)
|
||||
BbbCommonEnvCoreMsg(envelope, event)
|
||||
}
|
||||
|
||||
val msgEvent = build(meetingId, breakoutId, userId, redirectJoinURL, noRedirectJoinURL)
|
||||
val msgEvent = build(meetingId, breakoutId, userId, redirectJoinURL, redirectToHtml5JoinURL)
|
||||
outGW.send(msgEvent)
|
||||
|
||||
}
|
||||
|
@ -61,7 +61,7 @@ trait BreakoutRoomCreatedMsgHdlr extends BreakoutHdlrHelpers {
|
||||
|
||||
def sendBreakoutRoomsList(breakoutModel: BreakoutModel): BreakoutModel = {
|
||||
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)
|
||||
@ -88,7 +88,7 @@ trait BreakoutRoomCreatedMsgHdlr extends BreakoutHdlrHelpers {
|
||||
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)
|
||||
outGW.send(event)
|
||||
|
||||
|
@ -28,7 +28,7 @@ trait BreakoutRoomsListMsgHdlr {
|
||||
breakoutModel <- state.breakout
|
||||
} yield {
|
||||
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()
|
||||
broadcastEvent(rooms, ready)
|
||||
|
@ -46,7 +46,7 @@ trait CreateBreakoutRoomsCmdMsgHdlr extends RightsManagementTrait {
|
||||
val (internalId, externalId) = BreakoutRoomsUtil.createMeetingIds(liveMeeting.props.meetingProp.intId, 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)
|
||||
}
|
||||
|
||||
@ -55,6 +55,7 @@ trait CreateBreakoutRoomsCmdMsgHdlr extends RightsManagementTrait {
|
||||
breakout.id, breakout.name,
|
||||
liveMeeting.props.meetingProp.intId,
|
||||
breakout.sequence,
|
||||
breakout.freeJoin,
|
||||
breakout.voiceConf,
|
||||
msg.body.durationInMinutes,
|
||||
liveMeeting.props.password.moderatorPass,
|
||||
|
@ -6,6 +6,7 @@ class ChatApp2x(implicit val context: ActorContext)
|
||||
extends GetChatHistoryReqMsgHdlr
|
||||
with SendPublicMessagePubMsgHdlr
|
||||
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 defaultPubGroupChat = GroupChatFactory.create(chatId, chatId,
|
||||
GroupChatAccess.PUBLIC, createBy, Vector.empty, Vector.empty)
|
||||
val groupChats = state.groupChats.add(defaultPubGroupChat)
|
||||
state.update(groupChats)
|
||||
GroupChatFactory.create(MAIN_PUBLIC_CHAT, MAIN_PUBLIC_CHAT, GroupChatAccess.PUBLIC, createBy, Vector.empty, Vector.empty)
|
||||
}
|
||||
|
||||
def createTestPublicGroupChat(state: MeetingState2x): MeetingState2x = {
|
||||
@ -60,6 +57,10 @@ object GroupChatApp {
|
||||
state.update(groupChats)
|
||||
}
|
||||
|
||||
def getAllGroupChatsInMeeting(state: MeetingState2x): Vector[GroupChat] = {
|
||||
state.groupChats.getAllGroupChatsInMeeting()
|
||||
}
|
||||
|
||||
def genTestChatMsgHistory(chatId: String, state: MeetingState2x, userId: String, liveMeeting: LiveMeeting): MeetingState2x = {
|
||||
def addH(state: MeetingState2x, userId: String, liveMeeting: LiveMeeting, msg: GroupChatMsgFromUser): MeetingState2x = {
|
||||
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)
|
||||
extends CreateGroupChatReqMsgHdlr
|
||||
with CreateDefaultPublicGroupChat
|
||||
with GetGroupChatMsgsReqMsgHdlr
|
||||
with GetGroupChatsReqMsgHdlr
|
||||
with SendGroupChatMessageMsgHdlr {
|
||||
with SendGroupChatMessageMsgHdlr
|
||||
with SyncGetGroupChatsInfoMsgHdlr {
|
||||
|
||||
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.models.Polls
|
||||
import org.bigbluebutton.core.running.{ LiveMeeting }
|
||||
import org.bigbluebutton.core.models.Users2x
|
||||
|
||||
trait RespondToPollReqMsgHdlr {
|
||||
this: PollApp2x =>
|
||||
@ -34,12 +35,29 @@ trait RespondToPollReqMsgHdlr {
|
||||
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 {
|
||||
(pollId: String, updatedPoll: SimplePollResultOutVO) <- Polls.handleRespondToPollReqMsg(msg.header.userId, msg.body.pollId,
|
||||
msg.body.questionId, msg.body.answerId, liveMeeting)
|
||||
} yield {
|
||||
broadcastPollUpdatedEvent(msg, pollId, updatedPoll)
|
||||
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.models.PresentationPod
|
||||
import org.bigbluebutton.core.running.LiveMeeting
|
||||
import org.bigbluebutton.core2.message.senders.MsgBuilder
|
||||
|
||||
trait CreateNewPresentationPodPubMsgHdlr extends RightsManagementTrait {
|
||||
this: PresentationPodHdlrs =>
|
||||
@ -22,22 +23,14 @@ trait CreateNewPresentationPodPubMsgHdlr extends RightsManagementTrait {
|
||||
PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, bus.outGW, liveMeeting)
|
||||
state
|
||||
} 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 respMsg = buildCreateNewPresentationPodEvtMsg(
|
||||
val respMsg = MsgBuilder.buildCreateNewPresentationPodEvtMsg(
|
||||
liveMeeting.props.meetingProp.intId,
|
||||
resultPod.currentPresenter, resultPod.id
|
||||
resultPod.currentPresenter,
|
||||
resultPod.id,
|
||||
msg.header.userId
|
||||
)
|
||||
bus.outGW.send(respMsg)
|
||||
|
||||
|
@ -16,20 +16,7 @@ trait PresentationConversionCompletedSysPubMsgHdlr {
|
||||
liveMeeting: LiveMeeting, bus: MessageBus
|
||||
): MeetingState2x = {
|
||||
|
||||
def broadcastPresentationConversionCompletedEvtMsg(podId: String, userId: String, messageKey: String,
|
||||
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 meetingId = liveMeeting.props.meetingProp.intId
|
||||
|
||||
val pages = new collection.mutable.HashMap[String, PageVO]
|
||||
|
||||
@ -39,15 +26,20 @@ trait PresentationConversionCompletedSysPubMsgHdlr {
|
||||
pages += page.id -> page
|
||||
}
|
||||
|
||||
val pres = new PresentationInPod(msg.body.presentation.id, msg.body.presentation.name, msg.body.presentation.current,
|
||||
pages.toMap, msg.body.presentation.downloadable)
|
||||
val downloadable = 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 podId = msg.body.podId
|
||||
|
||||
val newState = for {
|
||||
pod <- PresentationPodsApp.getPresentationPod(state, podId)
|
||||
} 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)
|
||||
pods = pods.addPresentationToPod(pod.id, pres)
|
||||
|
@ -5,6 +5,7 @@ import akka.event.Logging
|
||||
|
||||
class PresentationPodHdlrs(implicit val context: ActorContext)
|
||||
extends CreateNewPresentationPodPubMsgHdlr
|
||||
with CreateDefaultPresentationPod
|
||||
with GetAllPresentationPodsReqMsgHdlr
|
||||
with SetCurrentPresentationPubMsgHdlr
|
||||
with PresentationConversionCompletedSysPubMsgHdlr
|
||||
|
@ -11,9 +11,8 @@ object PresentationPodsApp {
|
||||
PresentationPodFactory.create(creatorId)
|
||||
}
|
||||
|
||||
def createDefaultPresentationPod(state: MeetingState2x): MeetingState2x = {
|
||||
val podManager = state.presentationPodManager.addPod(PresentationPodFactory.createDefaultPod())
|
||||
state.update(podManager)
|
||||
def createDefaultPresentationPod(): PresentationPod = {
|
||||
PresentationPodFactory.createDefaultPod()
|
||||
}
|
||||
|
||||
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
|
||||
): MeetingState2x = {
|
||||
|
||||
val meetingId = liveMeeting.props.meetingProp.intId
|
||||
|
||||
if (filterPresentationMessage(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."
|
||||
PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, bus.outGW, liveMeeting)
|
||||
state
|
||||
} 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 presentationId = msg.body.presentationId
|
||||
@ -41,8 +29,10 @@ trait SetPresentationDownloadablePubMsgHdlr extends RightsManagementTrait {
|
||||
|
||||
val newState = for {
|
||||
pod <- PresentationPodsApp.getPresentationPod(state, podId)
|
||||
pres <- pod.getPresentation(presentationId)
|
||||
} 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)
|
||||
state.update(pods)
|
||||
|
@ -14,7 +14,7 @@ trait ChangeLockSettingsInMeetingCmdMsgHdlr extends RightsManagementTrait {
|
||||
|
||||
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 reason = "No permission to change lock settings"
|
||||
PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, outGW, liveMeeting)
|
||||
|
@ -1,7 +1,7 @@
|
||||
package org.bigbluebutton.core.apps.users
|
||||
|
||||
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.apps.{ PermissionCheck, RightsManagementTrait }
|
||||
|
||||
@ -20,8 +20,17 @@ trait ChangeUserRoleCmdMsgHdlr extends RightsManagementTrait {
|
||||
for {
|
||||
uvo <- Users2x.findWithIntId(liveMeeting.users2x, msg.body.userId)
|
||||
} 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)
|
||||
val event = buildUserRoleChangedEvtMsg(liveMeeting.props.meetingProp.intId, msg.body.userId,
|
||||
msg.body.changedBy, "MODERATOR")
|
||||
|
@ -12,10 +12,10 @@ trait GetRecordingStatusReqMsgHdlr {
|
||||
|
||||
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 envelope = BbbCoreEnvelope(GetRecordingStatusRespMsg.NAME, routing)
|
||||
val body = GetRecordingStatusRespMsgBody(recording, userId)
|
||||
val body = GetRecordingStatusRespMsgBody(recorded, recording, userId)
|
||||
val header = BbbClientMsgHeader(GetRecordingStatusRespMsg.NAME, meetingId, userId)
|
||||
val event = GetRecordingStatusRespMsg(header, body)
|
||||
|
||||
@ -23,7 +23,7 @@ trait GetRecordingStatusReqMsgHdlr {
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
@ -34,9 +34,9 @@ trait MuteUserCmdMsgHdlr extends RightsManagementTrait {
|
||||
requester <- Users2x.findWithIntId(liveMeeting.users2x, msg.header.userId)
|
||||
u <- VoiceUsers.findWithIntId(liveMeeting.voiceUsers, msg.body.userId)
|
||||
} 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) {
|
||||
// muting/unmuting self while not moderator and mic disabled. Do not allow.
|
||||
// unmuting self while not moderator and mic disabled. Do not allow.
|
||||
} else {
|
||||
if (u.muted != msg.body.mute) {
|
||||
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 guestStatus = msg.body.guestStatus
|
||||
|
||||
println("****** GUEST POLICY = " + guestPolicy + " guestStatus = " + guestStatus)
|
||||
|
||||
val regUser = RegisteredUsers.create(msg.body.intUserId, msg.body.extUserId,
|
||||
msg.body.name, msg.body.role, msg.body.authToken,
|
||||
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
|
||||
|
||||
import org.bigbluebutton.common2.msgs._
|
||||
import org.bigbluebutton.core.running.{ LiveMeeting, OutMsgRouter }
|
||||
import org.bigbluebutton.core2.MeetingStatus2x
|
||||
import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait }
|
||||
|
||||
trait SetRecordingStatusCmdMsgHdlr extends RightsManagementTrait {
|
||||
this: UsersApp =>
|
||||
|
||||
val liveMeeting: LiveMeeting
|
||||
val outGW: OutMsgRouter
|
||||
|
||||
def handleSetRecordingStatusCmdMsg(msg: SetRecordingStatusCmdMsg) {
|
||||
log.info("Change recording status. meetingId=" + liveMeeting.props.meetingProp.intId + " recording=" + msg.body.recording)
|
||||
|
||||
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)
|
||||
} else {
|
||||
if (liveMeeting.props.recordProp.allowStartStopRecording &&
|
||||
MeetingStatus2x.isRecording(liveMeeting.status) != msg.body.recording) {
|
||||
if (msg.body.recording) {
|
||||
MeetingStatus2x.recordingStarted(liveMeeting.status)
|
||||
} else {
|
||||
MeetingStatus2x.recordingStopped(liveMeeting.status)
|
||||
}
|
||||
|
||||
val event = buildRecordingStatusChangedEvtMsg(liveMeeting.props.meetingProp.intId, msg.body.setBy, msg.body.recording)
|
||||
outGW.send(event)
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
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.core.bus.BigBlueButtonEvent
|
||||
import org.bigbluebutton.core.api.SendRecordingTimerInternalMsg
|
||||
import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait }
|
||||
|
||||
trait SetRecordingStatusCmdMsgHdlr extends RightsManagementTrait {
|
||||
this: UsersApp =>
|
||||
|
||||
val liveMeeting: LiveMeeting
|
||||
val outGW: OutMsgRouter
|
||||
|
||||
def handleSetRecordingStatusCmdMsg(msg: SetRecordingStatusCmdMsg, state: MeetingState2x): MeetingState2x = {
|
||||
log.info("Change recording status. 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)
|
||||
}
|
||||
|
||||
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)
|
||||
state
|
||||
} else {
|
||||
if (liveMeeting.props.recordProp.allowStartStopRecording &&
|
||||
MeetingStatus2x.isRecording(liveMeeting.status) != msg.body.recording) {
|
||||
if (msg.body.recording) {
|
||||
MeetingStatus2x.recordingStarted(liveMeeting.status)
|
||||
} else {
|
||||
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 webUsers = users.map { u =>
|
||||
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,
|
||||
locked = u.locked, presenter = u.presenter, avatar = u.avatar)
|
||||
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,
|
||||
locked = u.locked,
|
||||
presenter = u.presenter,
|
||||
avatar = u.avatar,
|
||||
clientType = u.clientType
|
||||
)
|
||||
}
|
||||
|
||||
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 = {
|
||||
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, props.meetingProp.intId, userId)
|
||||
val envelope = BbbCoreEnvelope(UserBroadcastCamStoppedEvtMsg.NAME, routing)
|
||||
|
@ -14,23 +14,11 @@ trait UserJoinMeetingAfterReconnectReqMsgHdlr extends HandlerHelpers with Breako
|
||||
|
||||
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) {
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -13,7 +13,7 @@ trait UserJoinMeetingReqMsgHdlr extends HandlerHelpers with BreakoutHdlrHelpers
|
||||
val outGW: OutMsgRouter
|
||||
|
||||
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) {
|
||||
updateParentMeetingWithUsers()
|
||||
|
@ -1,11 +1,14 @@
|
||||
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.core.apps.presentationpod.PresentationPodsApp
|
||||
import org.bigbluebutton.core.domain.MeetingState2x
|
||||
import org.bigbluebutton.core.models.Users2x
|
||||
import org.bigbluebutton.core.running.{ MeetingActor, OutMsgRouter }
|
||||
import org.bigbluebutton.core.util.TimeUtil
|
||||
import org.bigbluebutton.core2.MeetingStatus2x
|
||||
import org.bigbluebutton.core2.message.senders.MsgBuilder
|
||||
|
||||
trait UserLeaveReqMsgHdlr {
|
||||
@ -35,11 +38,13 @@ trait UserLeaveReqMsgHdlr {
|
||||
} yield {
|
||||
log.info("User left meeting. meetingId=" + props.meetingProp.intId + " userId=" + u.intId + " user=" + u)
|
||||
|
||||
// stop the webcams of a user leaving
|
||||
handleUserBroadcastCamStopMsg(msg.body.userId)
|
||||
val authedUsers = Users2x.findAllAuthedUsers(liveMeeting.users2x)
|
||||
if (u.authed && authedUsers.isEmpty) {
|
||||
MeetingStatus2x.setLastAuthedUserLeftOn(liveMeeting.status)
|
||||
}
|
||||
|
||||
captionApp2x.handleUserLeavingMsg(msg.body.userId, liveMeeting, msgBus)
|
||||
stopAutoStartedRecording()
|
||||
stopRecordingIfAutoStart2x(outGW, liveMeeting, state)
|
||||
|
||||
// send a user left event for the clients to update
|
||||
val userLeftMeetingEvent = MsgBuilder.buildUserLeftMeetingEvtMsg(liveMeeting.props.meetingProp.intId, u.intId)
|
||||
|
@ -129,6 +129,8 @@ class UsersApp(
|
||||
with LogoutAndEndMeetingCmdMsgHdlr
|
||||
with MeetingActivityResponseCmdMsgHdlr
|
||||
with SetRecordingStatusCmdMsgHdlr
|
||||
with RecordAndClearPreviousMarkersCmdMsgHdlr
|
||||
with SendRecordingTimerInternalMsgHdlr
|
||||
with UpdateWebcamsOnlyForModeratorCmdMsgHdlr
|
||||
with GetRecordingStatusReqMsgHdlr
|
||||
with GetWebcamsOnlyForModeratorReqMsgHdlr
|
||||
|
@ -66,7 +66,7 @@ trait ValidateAuthTokenReqMsgHdlr extends HandlerHelpers {
|
||||
val webUsers = users.map { u =>
|
||||
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,
|
||||
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)
|
||||
|
@ -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 UserTalkingInVoiceConfEvtMsgHdlr
|
||||
with RecordingStartedVoiceConfEvtMsgHdlr
|
||||
with VoiceConfRunningEvtMsgHdlr {
|
||||
with VoiceConfRunningEvtMsgHdlr
|
||||
with SyncGetVoiceUsersMsgHdlr {
|
||||
|
||||
this: MeetingActor =>
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ case class BreakoutRoom2x(
|
||||
name: String,
|
||||
parentId: String,
|
||||
sequence: Int,
|
||||
freeJoin: Boolean,
|
||||
voiceConf: String,
|
||||
assignedUsers: Vector[String],
|
||||
users: Vector[BreakoutUser],
|
||||
|
@ -20,7 +20,9 @@ case class MeetingInactivityTracker(
|
||||
}
|
||||
|
||||
def hasRecentActivity(nowInMs: Long): Boolean = {
|
||||
nowInMs - lastActivityTimestampInMs < maxInactivityTimeoutInMs - warningBeforeMaxInMs
|
||||
val left = nowInMs - lastActivityTimestampInMs
|
||||
val right = maxInactivityTimeoutInMs - warningBeforeMaxInMs
|
||||
left < right
|
||||
}
|
||||
|
||||
def isMeetingInactive(nowInMs: Long): Boolean = {
|
||||
@ -35,10 +37,14 @@ case class MeetingInactivityTracker(
|
||||
case class MeetingExpiryTracker(
|
||||
startedOnInMs: Long,
|
||||
userHasJoined: Boolean,
|
||||
isBreakout: Boolean,
|
||||
lastUserLeftOnInMs: Option[Long],
|
||||
durationInMs: Long,
|
||||
meetingExpireIfNoUserJoinedInMs: Long,
|
||||
meetingExpireWhenLastUserLeftInMs: Long
|
||||
meetingExpireWhenLastUserLeftInMs: Long,
|
||||
userInactivityInspectTimerInMs: Long,
|
||||
userInactivityThresholdInMs: Long,
|
||||
userActivitySignResponseDelayInMs: Long
|
||||
) {
|
||||
def setUserHasJoined(): MeetingExpiryTracker = {
|
||||
if (!userHasJoined) {
|
||||
@ -63,11 +69,11 @@ case class MeetingExpiryTracker(
|
||||
}
|
||||
|
||||
def hasMeetingExpired(timestampInMs: Long): (Boolean, Option[String]) = {
|
||||
if (hasMeetingExpiredNeverBeenJoined(timestampInMs)) {
|
||||
if (hasMeetingExpiredNeverBeenJoined(timestampInMs) && !isBreakout) {
|
||||
(true, Some(MeetingEndReason.ENDED_WHEN_NOT_JOINED))
|
||||
} else if (meetingOverDuration(timestampInMs)) {
|
||||
(true, Some(MeetingEndReason.ENDED_AFTER_EXCEEDING_DURATION))
|
||||
} else if (hasMeetingExpiredAfterLastUserLeft(timestampInMs)) {
|
||||
} else if (hasMeetingExpiredAfterLastUserLeft(timestampInMs) && !isBreakout) {
|
||||
(true, Some(MeetingEndReason.ENDED_WHEN_LAST_USER_LEFT))
|
||||
} else {
|
||||
(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,
|
||||
breakout: Option[BreakoutModel],
|
||||
inactivityTracker: MeetingInactivityTracker,
|
||||
expiryTracker: MeetingExpiryTracker
|
||||
expiryTracker: MeetingExpiryTracker,
|
||||
recordingTracker: MeetingRecordingTracker
|
||||
) {
|
||||
|
||||
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(expiry: MeetingExpiryTracker): MeetingState2x = copy(expiryTracker = expiry)
|
||||
def update(inactivityTracker: MeetingInactivityTracker): MeetingState2x = copy(inactivityTracker = inactivityTracker)
|
||||
def update(recordingTracker: MeetingRecordingTracker): MeetingState2x = copy(recordingTracker = recordingTracker)
|
||||
}
|
||||
|
||||
object MeetingEndReason {
|
||||
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_LAST_USER_LEFT = "ENDED_WHEN_LAST_USER_LEFT"
|
||||
val ENDED_AFTER_USER_LOGGED_OUT = "ENDED_AFTER_USER_LOGGED_OUT"
|
||||
val ENDED_AFTER_EXCEEDING_DURATION = "ENDED_AFTER_EXCEEDING_DURATION"
|
||||
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, duration: Int) = status.breakoutRoomsdurationInMinutes = duration
|
||||
|
||||
def newBreakoutRoom(parentRoomId: String, id: String, externalMeetingId: String, name: String, sequence: Integer, voiceConfId: String,
|
||||
assignedUsers: Vector[String], breakoutRooms: BreakoutRooms): Option[BreakoutRoomVO] = {
|
||||
val brvo = new BreakoutRoomVO(id, externalMeetingId, name, parentRoomId, sequence, voiceConfId, assignedUsers, Vector())
|
||||
def newBreakoutRoom(parentRoomId: String, id: String, externalMeetingId: String, name: String, sequence: Integer, freeJoin: Boolean,
|
||||
voiceConfId: String, assignedUsers: Vector[String], breakoutRooms: BreakoutRooms): Option[BreakoutRoomVO] = {
|
||||
val brvo = new BreakoutRoomVO(id, externalMeetingId, name, parentRoomId, sequence, freeJoin, voiceConfId, assignedUsers, Vector())
|
||||
breakoutRooms.add(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 findAllPrivateChatsForUser(id: String) = chats.values.toVector filter (c =>
|
||||
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,
|
||||
|
@ -56,6 +56,13 @@ object RegisteredUsers {
|
||||
u
|
||||
}
|
||||
|
||||
def updateUserRole(users: RegisteredUsers, user: RegisteredUser,
|
||||
role: String): RegisteredUser = {
|
||||
val u = user.modify(_.role).setTo(role)
|
||||
users.save(u)
|
||||
u
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class RegisteredUsers {
|
||||
|
@ -1,6 +1,7 @@
|
||||
package org.bigbluebutton.core.models
|
||||
|
||||
import com.softwaremill.quicklens._
|
||||
import org.bigbluebutton.core.util.TimeUtil
|
||||
|
||||
object Users2x {
|
||||
def findWithIntId(users: Users2x, intId: String): Option[UserState] = {
|
||||
@ -26,6 +27,12 @@ object Users2x {
|
||||
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 = {
|
||||
val newUserState = modify(u)(_.role).setTo(newRole).modify(_.roleChangedOn).setTo(System.currentTimeMillis())
|
||||
users.save(newUserState)
|
||||
@ -103,6 +110,10 @@ object Users2x {
|
||||
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 = {
|
||||
users.updatePresenterGroup(users.presenterGroup.filterNot(_ == userIdToAdd).:+(userIdToAdd)) // ensure no repetition
|
||||
users.presenterGroup.contains(userIdToAdd)
|
||||
@ -202,9 +213,22 @@ class Users2x {
|
||||
|
||||
case class OldPresenter(userId: String, changedPresenterOn: Long)
|
||||
|
||||
case class UserState(intId: String, extId: String, name: String, role: String,
|
||||
guest: Boolean, authed: Boolean, guestStatus: String, emoji: String, locked: Boolean,
|
||||
presenter: Boolean, avatar: String, roleChangedOn: Long = System.currentTimeMillis())
|
||||
case class UserState(
|
||||
intId: String,
|
||||
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)
|
||||
|
||||
@ -232,4 +256,5 @@ object EjectReasonCode {
|
||||
val EJECT_USER = "user_requested_eject_reason"
|
||||
val SYSTEM_EJECT_USER = "system_requested_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)
|
||||
case GetPresenterGroupReqMsg.NAME =>
|
||||
routeGenericMsg[GetPresenterGroupReqMsg](envelope, jsonNode)
|
||||
case UserActivitySignCmdMsg.NAME =>
|
||||
routeGenericMsg[UserActivitySignCmdMsg](envelope, jsonNode)
|
||||
|
||||
// Poll
|
||||
case StartCustomPollReqMsg.NAME =>
|
||||
@ -184,7 +186,6 @@ class ReceivedJsonMsgHandlerActor(
|
||||
case GetWhiteboardAnnotationsReqMsg.NAME =>
|
||||
routeGenericMsg[GetWhiteboardAnnotationsReqMsg](envelope, jsonNode)
|
||||
case ClientToServerLatencyTracerMsg.NAME =>
|
||||
log.info("-- trace --" + jsonNode.toString)
|
||||
routeGenericMsg[ClientToServerLatencyTracerMsg](envelope, jsonNode)
|
||||
|
||||
// Presentation
|
||||
@ -254,6 +255,8 @@ class ReceivedJsonMsgHandlerActor(
|
||||
routeGenericMsg[SendPrivateMessagePubMsg](envelope, jsonNode)
|
||||
case ClearPublicChatHistoryPubMsg.NAME =>
|
||||
routeGenericMsg[ClearPublicChatHistoryPubMsg](envelope, jsonNode)
|
||||
case UserTypingPubMsg.NAME =>
|
||||
routeGenericMsg[UserTypingPubMsg](envelope, jsonNode)
|
||||
|
||||
// Meeting
|
||||
case EndMeetingSysCmdMsg.NAME =>
|
||||
@ -264,6 +267,8 @@ class ReceivedJsonMsgHandlerActor(
|
||||
routeGenericMsg[LogoutAndEndMeetingCmdMsg](envelope, jsonNode)
|
||||
case SetRecordingStatusCmdMsg.NAME =>
|
||||
routeGenericMsg[SetRecordingStatusCmdMsg](envelope, jsonNode)
|
||||
case RecordAndClearPreviousMarkersCmdMsg.NAME =>
|
||||
routeGenericMsg[RecordAndClearPreviousMarkersCmdMsg](envelope, jsonNode)
|
||||
case GetRecordingStatusReqMsg.NAME =>
|
||||
routeGenericMsg[GetRecordingStatusReqMsg](envelope, jsonNode)
|
||||
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.core2.MeetingStatus2x
|
||||
import org.bigbluebutton.core2.message.senders.{ MsgBuilder, UserJoinedMeetingEvtMsgBuilder }
|
||||
import org.bigbluebutton.core.util.TimeUtil
|
||||
|
||||
trait HandlerHelpers extends SystemConfiguration {
|
||||
|
||||
@ -25,7 +26,7 @@ trait HandlerHelpers extends SystemConfiguration {
|
||||
outGW.send(event)
|
||||
}
|
||||
|
||||
def userJoinMeeting(outGW: OutMsgRouter, authToken: String,
|
||||
def userJoinMeeting(outGW: OutMsgRouter, authToken: String, clientType: String,
|
||||
liveMeeting: LiveMeeting, state: MeetingState2x): MeetingState2x = {
|
||||
val nu = for {
|
||||
regUser <- RegisteredUsers.findWithToken(authToken, liveMeeting.registeredUsers)
|
||||
@ -41,7 +42,8 @@ trait HandlerHelpers extends SystemConfiguration {
|
||||
emoji = "none",
|
||||
presenter = false,
|
||||
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)
|
||||
outGW.send(event)
|
||||
startRecordingIfAutoStart2x(outGW, liveMeeting)
|
||||
val newState = startRecordingIfAutoStart2x(outGW, liveMeeting, state)
|
||||
if (!Users2x.hasPresenter(liveMeeting.users2x)) {
|
||||
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 =>
|
||||
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) &&
|
||||
liveMeeting.props.recordProp.autoStartRecording && Users2x.numUsers(liveMeeting.users2x) == 1) {
|
||||
|
||||
MeetingStatus2x.recordingStarted(liveMeeting.status)
|
||||
|
||||
val tracker = state.recordingTracker.startTimer(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)
|
||||
@ -83,8 +98,38 @@ trait HandlerHelpers extends SystemConfiguration {
|
||||
"system", MeetingStatus2x.isRecording(liveMeeting.status)
|
||||
)
|
||||
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 = {
|
||||
|
@ -4,11 +4,12 @@ import java.io.{ PrintWriter, StringWriter }
|
||||
|
||||
import akka.actor._
|
||||
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.users._
|
||||
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.common2.domain.DefaultProps
|
||||
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.meeting.{ SyncGetMeetingInfoRespMsgHdlr, ValidateConnAuthTokenSysMsgHdlr }
|
||||
import org.bigbluebutton.core.apps.users.ChangeLockSettingsInMeetingCmdMsgHdlr
|
||||
import org.bigbluebutton.core2.message.senders.MsgBuilder
|
||||
import org.bigbluebutton.core2.testdata.FakeTestData
|
||||
import org.bigbluebutton.core2.message.senders.{ MsgBuilder, Sender }
|
||||
|
||||
object MeetingActor {
|
||||
def props(
|
||||
props: DefaultProps,
|
||||
eventBus: InternalEventBus,
|
||||
outGW: OutMsgRouter,
|
||||
liveMeeting: LiveMeeting
|
||||
): Props =
|
||||
liveMeeting: LiveMeeting): Props =
|
||||
Props(classOf[MeetingActor], props, eventBus, outGW, liveMeeting)
|
||||
}
|
||||
|
||||
@ -51,34 +50,35 @@ class MeetingActor(
|
||||
val props: DefaultProps,
|
||||
val eventBus: InternalEventBus,
|
||||
val outGW: OutMsgRouter,
|
||||
val liveMeeting: LiveMeeting
|
||||
)
|
||||
extends BaseMeetingActor
|
||||
with GuestsApp
|
||||
with LayoutApp2x
|
||||
with VoiceApp2x
|
||||
with BreakoutApp2x
|
||||
with UsersApp2x
|
||||
val liveMeeting: LiveMeeting)
|
||||
extends BaseMeetingActor
|
||||
with SystemConfiguration
|
||||
with GuestsApp
|
||||
with LayoutApp2x
|
||||
with VoiceApp2x
|
||||
with BreakoutApp2x
|
||||
with UsersApp2x
|
||||
|
||||
with UserBroadcastCamStartMsgHdlr
|
||||
with UserJoinMeetingReqMsgHdlr
|
||||
with UserJoinMeetingAfterReconnectReqMsgHdlr
|
||||
with UserBroadcastCamStopMsgHdlr
|
||||
with UserConnectedToGlobalAudioMsgHdlr
|
||||
with UserDisconnectedFromGlobalAudioMsgHdlr
|
||||
with MuteAllExceptPresentersCmdMsgHdlr
|
||||
with MuteMeetingCmdMsgHdlr
|
||||
with IsMeetingMutedReqMsgHdlr
|
||||
with UserBroadcastCamStartMsgHdlr
|
||||
with UserJoinMeetingReqMsgHdlr
|
||||
with UserJoinMeetingAfterReconnectReqMsgHdlr
|
||||
with UserBroadcastCamStopMsgHdlr
|
||||
with UserConnectedToGlobalAudioMsgHdlr
|
||||
with UserDisconnectedFromGlobalAudioMsgHdlr
|
||||
with MuteAllExceptPresentersCmdMsgHdlr
|
||||
with MuteMeetingCmdMsgHdlr
|
||||
with IsMeetingMutedReqMsgHdlr
|
||||
|
||||
with EjectUserFromVoiceCmdMsgHdlr
|
||||
with EndMeetingSysCmdMsgHdlr
|
||||
with DestroyMeetingSysCmdMsgHdlr
|
||||
with SendTimeRemainingUpdateHdlr
|
||||
with SendBreakoutTimeRemainingMsgHdlr
|
||||
with ChangeLockSettingsInMeetingCmdMsgHdlr
|
||||
with SyncGetMeetingInfoRespMsgHdlr
|
||||
with ClientToServerLatencyTracerMsgHdlr
|
||||
with ValidateConnAuthTokenSysMsgHdlr {
|
||||
with EjectUserFromVoiceCmdMsgHdlr
|
||||
with EndMeetingSysCmdMsgHdlr
|
||||
with DestroyMeetingSysCmdMsgHdlr
|
||||
with SendTimeRemainingUpdateHdlr
|
||||
with SendBreakoutTimeRemainingMsgHdlr
|
||||
with ChangeLockSettingsInMeetingCmdMsgHdlr
|
||||
with SyncGetMeetingInfoRespMsgHdlr
|
||||
with ClientToServerLatencyTracerMsgHdlr
|
||||
with ValidateConnAuthTokenSysMsgHdlr
|
||||
with UserActivitySignCmdMsgHdlr {
|
||||
|
||||
override val supervisorStrategy = OneForOneStrategy(maxNrOfRetries = 10, withinTimeRange = 1 minute) {
|
||||
case e: Exception => {
|
||||
@ -96,8 +96,7 @@ class MeetingActor(
|
||||
*/
|
||||
var actorMonitor = context.actorOf(
|
||||
MeetingActorAudit.props(props, eventBus, outGW),
|
||||
"actorMonitor-" + props.meetingProp.intId
|
||||
)
|
||||
"actorMonitor-" + props.meetingProp.intId)
|
||||
|
||||
val msgBus = MessageBus(eventBus, outGW)
|
||||
|
||||
@ -119,40 +118,45 @@ class MeetingActor(
|
||||
TimeUtil.minutesToMillis(props.durationProps.warnMinutesBeforeMax),
|
||||
lastActivityTimestampInMs = TimeUtil.timeNowInMs(),
|
||||
warningSent = false,
|
||||
warningSentOnTimestampInMs = 0L
|
||||
)
|
||||
warningSentOnTimestampInMs = 0L)
|
||||
|
||||
val expiryTracker = new MeetingExpiryTracker(
|
||||
startedOnInMs = TimeUtil.timeNowInMs(),
|
||||
userHasJoined = false,
|
||||
isBreakout = props.meetingProp.isBreakout,
|
||||
lastUserLeftOnInMs = None,
|
||||
durationInMs = TimeUtil.minutesToMillis(props.durationProps.duration),
|
||||
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(
|
||||
new GroupChats(Map.empty),
|
||||
new PresentationPodManager(Map.empty),
|
||||
None,
|
||||
inactivityTracker,
|
||||
expiryTracker
|
||||
)
|
||||
expiryTracker,
|
||||
recordingTracker)
|
||||
|
||||
var lastRttTestSentOn = System.currentTimeMillis()
|
||||
|
||||
// Create a default publish group chat
|
||||
state = GroupChatApp.createDefaultPublicGroupChat(GroupChatApp.MAIN_PUBLIC_CHAT, state)
|
||||
//state = GroupChatApp.genTestChatMsgHistory(GroupChatApp.MAIN_PUBLIC_CHAT, state, BbbSystemConst.SYSTEM_USER, liveMeeting)
|
||||
// Create a default public group chat
|
||||
state = groupChatApp.handleCreateDefaultPublicGroupChat(state, liveMeeting, msgBus)
|
||||
|
||||
// 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.genTestChatMsgHistory("TEST_GROUP_CHAT", state, BbbSystemConst.SYSTEM_USER, liveMeeting)
|
||||
|
||||
log.debug("NUM GROUP CHATS = " + state.groupChats.findAllPublicChats().length)
|
||||
|
||||
// Create a default Presentation Pod
|
||||
state = PresentationPodsApp.createDefaultPresentationPod(state)
|
||||
state = presentationPodsApp.handleCreateDefaultPresentationPod(state, liveMeeting, msgBus)
|
||||
|
||||
log.debug("NUM Presentation Pods = " + state.presentationPodManager.getNumberOfPods())
|
||||
|
||||
// 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
|
||||
MeetingStatus2x.setWebcamsOnlyForModerator(liveMeeting.status, liveMeeting.props.usersProp.webcamsOnlyForModerator)
|
||||
|
||||
/*******************************************************************/
|
||||
/** *****************************************************************/
|
||||
// Helper to create fake users for testing (ralam jan 5, 2018)
|
||||
//object FakeTestData extends FakeTestData
|
||||
//FakeTestData.createFakeUsers(liveMeeting)
|
||||
/*******************************************************************/
|
||||
/** *****************************************************************/
|
||||
|
||||
def receive = {
|
||||
//=============================
|
||||
@ -206,148 +210,216 @@ class MeetingActor(
|
||||
// Screenshare
|
||||
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 = {
|
||||
val tracker = state.inactivityTracker.updateLastActivityTimestamp(TimeUtil.timeNowInMs())
|
||||
state = state.update(tracker)
|
||||
|
||||
msg.core match {
|
||||
case m: EndMeetingSysCmdMsg => handleEndMeeting(m, state)
|
||||
case m: EndMeetingSysCmdMsg => handleEndMeeting(m, state)
|
||||
|
||||
// Users
|
||||
case m: ValidateAuthTokenReqMsg => state = usersApp.handleValidateAuthTokenReqMsg(m, state)
|
||||
case m: UserJoinMeetingReqMsg => state = handleUserJoinMeetingReqMsg(m, state)
|
||||
case m: ValidateAuthTokenReqMsg => state = usersApp.handleValidateAuthTokenReqMsg(m, state)
|
||||
case m: UserJoinMeetingReqMsg => state = handleUserJoinMeetingReqMsg(m, state)
|
||||
case m: UserJoinMeetingAfterReconnectReqMsg => state = handleUserJoinMeetingAfterReconnectReqMsg(m, state)
|
||||
case m: UserLeaveReqMsg => state = handleUserLeaveReqMsg(m, state)
|
||||
case m: UserBroadcastCamStartMsg => handleUserBroadcastCamStartMsg(m)
|
||||
case m: UserBroadcastCamStopMsg => handleUserBroadcastCamStopMsg(m)
|
||||
case m: UserJoinedVoiceConfEvtMsg => handleUserJoinedVoiceConfEvtMsg(m)
|
||||
case m: MeetingActivityResponseCmdMsg => state = usersApp.handleMeetingActivityResponseCmdMsg(m, state)
|
||||
case m: UserLeaveReqMsg => state = handleUserLeaveReqMsg(m, state)
|
||||
case m: UserBroadcastCamStartMsg => handleUserBroadcastCamStartMsg(m)
|
||||
case m: UserBroadcastCamStopMsg => handleUserBroadcastCamStopMsg(m)
|
||||
case m: UserJoinedVoiceConfEvtMsg => handleUserJoinedVoiceConfEvtMsg(m)
|
||||
case m: MeetingActivityResponseCmdMsg =>
|
||||
state = usersApp.handleMeetingActivityResponseCmdMsg(m, state)
|
||||
state = updateInactivityTracker(state)
|
||||
case m: LogoutAndEndMeetingCmdMsg => usersApp.handleLogoutAndEndMeetingCmdMsg(m, state)
|
||||
case m: SetRecordingStatusCmdMsg => usersApp.handleSetRecordingStatusCmdMsg(m)
|
||||
case m: GetWebcamsOnlyForModeratorReqMsg => usersApp.handleGetWebcamsOnlyForModeratorReqMsg(m)
|
||||
case m: SetRecordingStatusCmdMsg =>
|
||||
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: GetRecordingStatusReqMsg => usersApp.handleGetRecordingStatusReqMsg(m)
|
||||
case m: ChangeUserEmojiCmdMsg => handleChangeUserEmojiCmdMsg(m)
|
||||
case m: GetRecordingStatusReqMsg => usersApp.handleGetRecordingStatusReqMsg(m)
|
||||
case m: ChangeUserEmojiCmdMsg => handleChangeUserEmojiCmdMsg(m)
|
||||
|
||||
// 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.
|
||||
case m: EjectUserFromMeetingSysMsg => usersApp.handleEjectUserFromMeetingSysMsg(m)
|
||||
case m: GetUsersMeetingReqMsg => usersApp.handleGetUsersMeetingReqMsg(m)
|
||||
case m: ChangeUserRoleCmdMsg => usersApp.handleChangeUserRoleCmdMsg(m)
|
||||
case m: GetUsersMeetingReqMsg => usersApp.handleGetUsersMeetingReqMsg(m)
|
||||
case m: ChangeUserRoleCmdMsg =>
|
||||
usersApp.handleChangeUserRoleCmdMsg(m)
|
||||
updateUserLastActivity(m.body.changedBy)
|
||||
|
||||
// Whiteboard
|
||||
case m: SendCursorPositionPubMsg => wbApp.handle(m, liveMeeting, msgBus)
|
||||
case m: ClearWhiteboardPubMsg => wbApp.handle(m, liveMeeting, msgBus)
|
||||
case m: UndoWhiteboardPubMsg => wbApp.handle(m, liveMeeting, msgBus)
|
||||
case m: ModifyWhiteboardAccessPubMsg => wbApp.handle(m, liveMeeting, msgBus)
|
||||
case m: SendCursorPositionPubMsg => wbApp.handle(m, liveMeeting, msgBus)
|
||||
case m: ClearWhiteboardPubMsg => wbApp.handle(m, liveMeeting, msgBus)
|
||||
case m: UndoWhiteboardPubMsg => wbApp.handle(m, liveMeeting, msgBus)
|
||||
case m: ModifyWhiteboardAccessPubMsg => wbApp.handle(m, liveMeeting, msgBus)
|
||||
case m: SendWhiteboardAnnotationPubMsg => wbApp.handle(m, liveMeeting, msgBus)
|
||||
case m: GetWhiteboardAnnotationsReqMsg => wbApp.handle(m, liveMeeting, msgBus)
|
||||
|
||||
case m: ClientToServerLatencyTracerMsg => handleClientToServerLatencyTracerMsg(m)
|
||||
|
||||
// Poll
|
||||
case m: StartPollReqMsg => 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
|
||||
case m: StopPollReqMsg => pollApp.handle(m, state, liveMeeting, msgBus) // passing state but not modifying it
|
||||
case m: ShowPollResultReqMsg => pollApp.handle(m, state, liveMeeting, msgBus) // passing state but not modifying it
|
||||
case m: StartPollReqMsg =>
|
||||
pollApp.handle(m, state, liveMeeting, msgBus) // passing state but not modifying it
|
||||
updateUserLastActivity(m.body.requesterId)
|
||||
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: RespondToPollReqMsg => pollApp.handle(m, liveMeeting, msgBus)
|
||||
case m: RespondToPollReqMsg =>
|
||||
pollApp.handle(m, liveMeeting, msgBus)
|
||||
updateUserLastActivity(m.body.requesterId)
|
||||
|
||||
// Breakout
|
||||
case m: BreakoutRoomsListMsg => state = handleBreakoutRoomsListMsg(m, state)
|
||||
case m: CreateBreakoutRoomsCmdMsg => state = handleCreateBreakoutRoomsCmdMsg(m, state)
|
||||
case m: EndAllBreakoutRoomsMsg => state = handleEndAllBreakoutRoomsMsg(m, state)
|
||||
case m: RequestBreakoutJoinURLReqMsg => state = handleRequestBreakoutJoinURLReqMsg(m, state)
|
||||
case m: BreakoutRoomsListMsg => state = handleBreakoutRoomsListMsg(m, state)
|
||||
case m: CreateBreakoutRoomsCmdMsg => state = handleCreateBreakoutRoomsCmdMsg(m, state)
|
||||
case m: EndAllBreakoutRoomsMsg => state = handleEndAllBreakoutRoomsMsg(m, state)
|
||||
case m: RequestBreakoutJoinURLReqMsg => state = handleRequestBreakoutJoinURLReqMsg(m, state)
|
||||
case m: TransferUserToMeetingRequestMsg => state = handleTransferUserToMeetingRequestMsg(m, state)
|
||||
|
||||
// Voice
|
||||
case m: UserLeftVoiceConfEvtMsg => handleUserLeftVoiceConfEvtMsg(m)
|
||||
case m: UserMutedInVoiceConfEvtMsg => handleUserMutedInVoiceConfEvtMsg(m)
|
||||
case m: UserTalkingInVoiceConfEvtMsg => handleUserTalkingInVoiceConfEvtMsg(m)
|
||||
case m: UserLeftVoiceConfEvtMsg => handleUserLeftVoiceConfEvtMsg(m)
|
||||
case m: UserMutedInVoiceConfEvtMsg => handleUserMutedInVoiceConfEvtMsg(m)
|
||||
case m: UserTalkingInVoiceConfEvtMsg =>
|
||||
state = updateInactivityTracker(state)
|
||||
updateVoiceUserLastActivity(m.body.voiceUserId)
|
||||
handleUserTalkingInVoiceConfEvtMsg(m)
|
||||
|
||||
case m: RecordingStartedVoiceConfEvtMsg => handleRecordingStartedVoiceConfEvtMsg(m)
|
||||
case m: MuteUserCmdMsg => usersApp.handleMuteUserCmdMsg(m)
|
||||
case m: MuteAllExceptPresentersCmdMsg => handleMuteAllExceptPresentersCmdMsg(m)
|
||||
case m: MuteUserCmdMsg =>
|
||||
usersApp.handleMuteUserCmdMsg(m)
|
||||
updateUserLastActivity(m.body.mutedBy)
|
||||
case m: MuteAllExceptPresentersCmdMsg =>
|
||||
handleMuteAllExceptPresentersCmdMsg(m)
|
||||
updateUserLastActivity(m.body.mutedBy)
|
||||
case m: EjectUserFromVoiceCmdMsg => handleEjectUserFromVoiceCmdMsg(m)
|
||||
case m: IsMeetingMutedReqMsg => handleIsMeetingMutedReqMsg(m)
|
||||
case m: MuteMeetingCmdMsg => handleMuteMeetingCmdMsg(m)
|
||||
case m: UserConnectedToGlobalAudioMsg => handleUserConnectedToGlobalAudioMsg(m)
|
||||
case m: IsMeetingMutedReqMsg => handleIsMeetingMutedReqMsg(m)
|
||||
case m: MuteMeetingCmdMsg =>
|
||||
handleMuteMeetingCmdMsg(m)
|
||||
updateUserLastActivity(m.body.mutedBy)
|
||||
case m: UserConnectedToGlobalAudioMsg => handleUserConnectedToGlobalAudioMsg(m)
|
||||
case m: UserDisconnectedFromGlobalAudioMsg => handleUserDisconnectedFromGlobalAudioMsg(m)
|
||||
case m: VoiceConfRunningEvtMsg => handleVoiceConfRunningEvtMsg(m)
|
||||
case m: VoiceConfRunningEvtMsg => handleVoiceConfRunningEvtMsg(m)
|
||||
|
||||
// Layout
|
||||
case m: GetCurrentLayoutReqMsg => handleGetCurrentLayoutReqMsg(m)
|
||||
case m: BroadcastLayoutMsg => handleBroadcastLayoutMsg(m)
|
||||
case m: GetCurrentLayoutReqMsg => handleGetCurrentLayoutReqMsg(m)
|
||||
case m: BroadcastLayoutMsg => handleBroadcastLayoutMsg(m)
|
||||
|
||||
// Lock Settings
|
||||
case m: ChangeLockSettingsInMeetingCmdMsg => handleSetLockSettings(m)
|
||||
case m: LockUserInMeetingCmdMsg => handleLockUserInMeetingCmdMsg(m)
|
||||
case m: LockUsersInMeetingCmdMsg => handleLockUsersInMeetingCmdMsg(m)
|
||||
case m: GetLockSettingsReqMsg => handleGetLockSettingsReqMsg(m)
|
||||
case m: ChangeLockSettingsInMeetingCmdMsg =>
|
||||
handleSetLockSettings(m)
|
||||
updateUserLastActivity(m.body.setBy)
|
||||
case m: LockUserInMeetingCmdMsg => handleLockUserInMeetingCmdMsg(m)
|
||||
case m: LockUsersInMeetingCmdMsg => handleLockUsersInMeetingCmdMsg(m)
|
||||
case m: GetLockSettingsReqMsg => handleGetLockSettingsReqMsg(m)
|
||||
|
||||
// Presentation
|
||||
case m: PreuploadedPresentationsSysPubMsg => presentationApp2x.handle(m, liveMeeting, msgBus)
|
||||
case m: AssignPresenterReqMsg => state = handlePresenterChange(m, state)
|
||||
case m: PreuploadedPresentationsSysPubMsg => presentationApp2x.handle(m, liveMeeting, msgBus)
|
||||
case m: AssignPresenterReqMsg => state = handlePresenterChange(m, state)
|
||||
|
||||
// Presentation Pods
|
||||
case m: CreateNewPresentationPodPubMsg => 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: SetCurrentPresentationPubMsg => 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: GetAllPresentationPodsReqMsg => 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: SetCurrentPagePubMsg => 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: SetPresentationDownloadablePubMsg => 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: PresentationPageCountErrorSysPubMsg => 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: SetCurrentPagePubMsg => 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: SetPresentationDownloadablePubMsg => 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: PresentationPageCountErrorSysPubMsg => 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)
|
||||
|
||||
// Caption
|
||||
case m: EditCaptionHistoryPubMsg => captionApp2x.handle(m, liveMeeting, msgBus)
|
||||
case m: UpdateCaptionOwnerPubMsg => captionApp2x.handle(m, liveMeeting, msgBus)
|
||||
case m: SendCaptionHistoryReqMsg => captionApp2x.handle(m, liveMeeting, msgBus)
|
||||
case m: EditCaptionHistoryPubMsg => captionApp2x.handle(m, liveMeeting, msgBus)
|
||||
case m: UpdateCaptionOwnerPubMsg => captionApp2x.handle(m, liveMeeting, msgBus)
|
||||
case m: SendCaptionHistoryReqMsg => captionApp2x.handle(m, liveMeeting, msgBus)
|
||||
|
||||
// SharedNotes
|
||||
case m: GetSharedNotesPubMsg => sharedNotesApp2x.handle(m, liveMeeting, msgBus)
|
||||
case m: SyncSharedNotePubMsg => sharedNotesApp2x.handle(m, liveMeeting, msgBus)
|
||||
case m: ClearSharedNotePubMsg => sharedNotesApp2x.handle(m, liveMeeting, msgBus)
|
||||
case m: UpdateSharedNoteReqMsg => sharedNotesApp2x.handle(m, liveMeeting, msgBus)
|
||||
case m: CreateSharedNoteReqMsg => sharedNotesApp2x.handle(m, liveMeeting, msgBus)
|
||||
case m: DestroySharedNoteReqMsg => sharedNotesApp2x.handle(m, liveMeeting, msgBus)
|
||||
case m: GetSharedNotesPubMsg => sharedNotesApp2x.handle(m, liveMeeting, msgBus)
|
||||
case m: SyncSharedNotePubMsg => sharedNotesApp2x.handle(m, liveMeeting, msgBus)
|
||||
case m: ClearSharedNotePubMsg => sharedNotesApp2x.handle(m, liveMeeting, msgBus)
|
||||
case m: UpdateSharedNoteReqMsg => sharedNotesApp2x.handle(m, liveMeeting, msgBus)
|
||||
case m: CreateSharedNoteReqMsg => sharedNotesApp2x.handle(m, liveMeeting, msgBus)
|
||||
case m: DestroySharedNoteReqMsg => sharedNotesApp2x.handle(m, liveMeeting, msgBus)
|
||||
|
||||
// Guests
|
||||
case m: GetGuestsWaitingApprovalReqMsg => handleGetGuestsWaitingApprovalReqMsg(m)
|
||||
case m: SetGuestPolicyCmdMsg => handleSetGuestPolicyMsg(m)
|
||||
case m: GuestsWaitingApprovedMsg => handleGuestsWaitingApprovedMsg(m)
|
||||
case m: GetGuestPolicyReqMsg => handleGetGuestPolicyReqMsg(m)
|
||||
case m: GetGuestsWaitingApprovalReqMsg => handleGetGuestsWaitingApprovalReqMsg(m)
|
||||
case m: SetGuestPolicyCmdMsg => handleSetGuestPolicyMsg(m)
|
||||
case m: GuestsWaitingApprovedMsg => handleGuestsWaitingApprovedMsg(m)
|
||||
case m: GetGuestPolicyReqMsg => handleGetGuestPolicyReqMsg(m)
|
||||
|
||||
// Chat
|
||||
case m: GetChatHistoryReqMsg => chatApp2x.handle(m, liveMeeting, msgBus)
|
||||
case m: SendPublicMessagePubMsg => chatApp2x.handle(m, liveMeeting, msgBus)
|
||||
case m: SendPrivateMessagePubMsg => chatApp2x.handle(m, liveMeeting, msgBus)
|
||||
case m: ClearPublicChatHistoryPubMsg => state = chatApp2x.handle(m, state, liveMeeting, msgBus)
|
||||
case m: GetChatHistoryReqMsg => chatApp2x.handle(m, liveMeeting, msgBus)
|
||||
case m: SendPublicMessagePubMsg =>
|
||||
chatApp2x.handle(m, 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
|
||||
case m: ScreenshareStartedVoiceConfEvtMsg => screenshareApp2x.handle(m, liveMeeting, msgBus)
|
||||
case m: ScreenshareStoppedVoiceConfEvtMsg => screenshareApp2x.handle(m, liveMeeting, msgBus)
|
||||
case m: ScreenshareStartedVoiceConfEvtMsg => screenshareApp2x.handle(m, liveMeeting, msgBus)
|
||||
case m: ScreenshareStoppedVoiceConfEvtMsg => screenshareApp2x.handle(m, liveMeeting, msgBus)
|
||||
case m: ScreenshareRtmpBroadcastStartedVoiceConfEvtMsg => 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
|
||||
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: GetGroupChatsReqMsg => state = groupChatApp.handle(m, state, liveMeeting, msgBus)
|
||||
case m: SendGroupChatMessageMsg => 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)
|
||||
updateUserLastActivity(m.body.msg.sender.id)
|
||||
|
||||
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
|
||||
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 screen sharing info
|
||||
|
||||
}
|
||||
|
||||
def handlePresenterChange(msg: AssignPresenterReqMsg, state: MeetingState2x): MeetingState2x = {
|
||||
@ -377,8 +455,7 @@ class MeetingActor(
|
||||
screenshareApp2x.handleScreenshareStoppedVoiceConfEvtMsg(
|
||||
liveMeeting.props.voiceProp.voiceConf,
|
||||
liveMeeting.props.screenshareProps.screenshareConf,
|
||||
liveMeeting, msgBus
|
||||
)
|
||||
liveMeeting, msgBus)
|
||||
|
||||
newState
|
||||
|
||||
@ -409,6 +486,10 @@ class MeetingActor(
|
||||
|
||||
sendRttTraceTest()
|
||||
setRecordingChapterBreak()
|
||||
|
||||
processUserInactivityAudit()
|
||||
|
||||
checkIfNeetToEndMeetingWhenNoAuthedUsers(liveMeeting)
|
||||
}
|
||||
|
||||
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) {
|
||||
|
||||
}
|
||||
@ -474,8 +574,7 @@ class MeetingActor(
|
||||
|
||||
val event = buildRecordingStatusChangedEvtMsg(
|
||||
liveMeeting.props.meetingProp.intId,
|
||||
"system", MeetingStatus2x.isRecording(liveMeeting.status)
|
||||
)
|
||||
"system", MeetingStatus2x.isRecording(liveMeeting.status))
|
||||
outGW.send(event)
|
||||
|
||||
}
|
||||
@ -499,10 +598,50 @@ class MeetingActor(
|
||||
|
||||
val event = buildRecordingStatusChangedEvtMsg(
|
||||
liveMeeting.props.meetingProp.intId,
|
||||
"system", MeetingStatus2x.isRecording(liveMeeting.status)
|
||||
)
|
||||
"system", MeetingStatus2x.isRecording(liveMeeting.status))
|
||||
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,
|
||||
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: StartRecordingVoiceConfSysMsg => logMessage(msg)
|
||||
case m: StopRecordingVoiceConfSysMsg => logMessage(msg)
|
||||
//case m: UpdateRecordingTimerEvtMsg => logMessage(msg)
|
||||
case m: RecordAndClearPreviousMarkersCmdMsg => 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: UserActivitySignCmdMsg => logMessage(msg)
|
||||
case m: UserInactivityInspectMsg => logMessage(msg)
|
||||
|
||||
case m: ChangeUserRoleCmdMsg => logMessage(msg)
|
||||
|
||||
// Breakout
|
||||
case m: BreakoutRoomEndedEvtMsg => logMessage(msg)
|
||||
|
@ -25,6 +25,9 @@ class FromAkkaAppsMsgSenderActor(msgSender: MessageSender)
|
||||
case SyncGetPresentationPodsRespMsg.NAME => msgSender.send(toHTML5RedisChannel, json)
|
||||
case SyncGetMeetingInfoRespMsg.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
|
||||
case ScreenshareStartRtmpBroadcastVoiceConfMsg.NAME =>
|
||||
@ -75,8 +78,6 @@ class FromAkkaAppsMsgSenderActor(msgSender: MessageSender)
|
||||
msgSender.send(fromAkkaAppsPresRedisChannel, json)
|
||||
case RemovePresentationEvtMsg.NAME =>
|
||||
msgSender.send(fromAkkaAppsPresRedisChannel, json)
|
||||
case SetPresentationDownloadableEvtMsg.NAME =>
|
||||
msgSender.send(fromAkkaAppsPresRedisChannel, json)
|
||||
case SetCurrentPresentationEvtMsg.NAME =>
|
||||
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
|
||||
// Poll Record Event
|
||||
case UserRespondedToPollRecordMsg.NAME =>
|
||||
|
@ -2,6 +2,8 @@ package org.bigbluebutton.core2
|
||||
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
import org.bigbluebutton.core.util.TimeUtil
|
||||
|
||||
case class Permissions(
|
||||
disableCam: Boolean = false,
|
||||
disableMic: Boolean = false,
|
||||
@ -45,6 +47,13 @@ object MeetingStatus2x {
|
||||
def recordingStopped(status: MeetingStatus2x) = status.recording = false
|
||||
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 = {
|
||||
val vrs = new VoiceRecordingStream(stream, recording = false, createdOn = System.currentTimeMillis, ackedOn = None, stoppedOn = None)
|
||||
status2x.voiceRecordings += vrs.stream -> vrs
|
||||
@ -115,6 +124,8 @@ class MeetingStatus2x {
|
||||
|
||||
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])
|
||||
|
@ -1,7 +1,7 @@
|
||||
package org.bigbluebutton.core2.message.handlers
|
||||
|
||||
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.running.{ LiveMeeting, OutMsgRouter }
|
||||
import org.bigbluebutton.core.util.TimeUtil
|
||||
|
@ -156,6 +156,16 @@ object MsgBuilder {
|
||||
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 = {
|
||||
val routing = Routing.addMsgToClientRouting(MessageTypes.SYSTEM, meetingId, "not-used")
|
||||
val envelope = BbbCoreEnvelope(DisconnectAllClientsSysMsg.NAME, routing)
|
||||
@ -208,6 +218,16 @@ object MsgBuilder {
|
||||
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 = {
|
||||
val routing = collection.immutable.HashMap("sender" -> "bbb-apps-akka")
|
||||
val envelope = BbbCoreEnvelope(CheckAlivePongSysMsg.NAME, routing)
|
||||
@ -286,4 +306,15 @@ object MsgBuilder {
|
||||
val event = StopRecordingVoiceConfSysMsg(header, body)
|
||||
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,
|
||||
ejectedBy: String, reason: String,
|
||||
reasonCode: String, outGW: OutMsgRouter): Unit = {
|
||||
val ejectFromMeetingClientEvent = MsgBuilder.buildUserEjectedFromMeetingEvtMsg(
|
||||
meetingId, userId, ejectedBy, reason, reasonCode
|
||||
)
|
||||
val ejectFromMeetingClientEvent = MsgBuilder.buildUserEjectedFromMeetingEvtMsg(meetingId, userId, ejectedBy, reason, reasonCode)
|
||||
outGW.send(ejectFromMeetingClientEvent)
|
||||
}
|
||||
|
||||
def sendDisconnectClientSysMsg(meetingId: String, userId: String,
|
||||
ejectedBy: String, reason: String, outGW: OutMsgRouter): Unit = {
|
||||
val ejectFromMeetingSystemEvent = MsgBuilder.buildDisconnectClientSysMsg(
|
||||
meetingId, userId, ejectedBy, reason
|
||||
)
|
||||
val ejectFromMeetingSystemEvent = MsgBuilder.buildDisconnectClientSysMsg(meetingId, userId, ejectedBy, reason)
|
||||
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,
|
||||
role = userState.role, guest = userState.guest, authed = userState.authed,
|
||||
guestStatus = userState.guestStatus, emoji = userState.emoji,
|
||||
presenter = userState.presenter, locked = userState.locked, avatar = userState.avatar)
|
||||
guestStatus = userState.guestStatus,
|
||||
emoji = userState.emoji,
|
||||
presenter = userState.presenter, locked = userState.locked, avatar = userState.avatar,
|
||||
clientType = userState.clientType)
|
||||
|
||||
val event = UserJoinedMeetingEvtMsg(meetingId, userState.intId, body)
|
||||
|
||||
|
@ -68,7 +68,6 @@ trait FakeTestData {
|
||||
def createFakeUser(liveMeeting: LiveMeeting, regUser: RegisteredUser): UserState = {
|
||||
UserState(intId = regUser.id, extId = regUser.externId, name = regUser.name, role = regUser.role,
|
||||
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
|
||||
case m: RecordingStatusChangedEvtMsg => handleRecordingStatusChangedEvtMsg(m)
|
||||
case m: RecordStatusResetSysMsg => handleRecordStatusResetSysMsg(m)
|
||||
case m: WebcamsOnlyForModeratorChangedEvtMsg => handleWebcamsOnlyForModeratorChangedEvtMsg(m)
|
||||
case m: EndAndKickAllSysMsg => handleEndAndKickAllSysMsg(m)
|
||||
|
||||
@ -463,6 +464,15 @@ class RedisRecorderActor(val system: ActorSystem)
|
||||
|
||||
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) {
|
||||
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"?>
|
||||
<configuration>
|
||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>%date{ISO8601} %-5level %logger{36} %X{akkaSource} - %msg%n</pattern>
|
||||
</encoder>
|
||||
<layout class="ch.qos.logback.classic.PatternLayout">
|
||||
<Pattern>%d{"yyyy-MM-dd'T'HH:mm:ss.SSSXXX"} %-5level %logger{35} - %msg%n</Pattern>
|
||||
</layout>
|
||||
</appender>
|
||||
|
||||
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<File>logs/bbb-fsesl-akka.log</File>
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<FileNamePattern>logs/bbb-fsesl-akka.%d{yyyy-MM-dd}.log</FileNamePattern>
|
||||
<!-- keep 30 days worth of history -->
|
||||
<MaxHistory>5</MaxHistory>
|
||||
<!-- keep 14 days worth of history -->
|
||||
<MaxHistory>14</MaxHistory>
|
||||
</rollingPolicy>
|
||||
<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>
|
||||
</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.setFFmpegPath(FFMPEG_PATH);
|
||||
ffmpeg.setInput(input);
|
||||
|
||||
ffmpeg.setProtocolWhitelist("file,udp,rtp");
|
||||
ffmpeg.setLoglevel("quiet");
|
||||
ffmpeg.setOutput(outputLive);
|
||||
ffmpeg.addRtmpOutputConnectionParameter(meetingId);
|
||||
@ -570,7 +570,7 @@ public class VideoTranscoder extends UntypedActor implements ProcessMonitorObser
|
||||
if(currentFFmpegRestartNumber == MAX_RESTARTINGS_NUMBER) {
|
||||
long timeInterval = System.currentTimeMillis() - lastFFmpegRestartTime;
|
||||
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.");
|
||||
return true;
|
||||
}
|
||||
|
@ -29,6 +29,8 @@ public class FFmpegCommand {
|
||||
private int frameRate;
|
||||
private String frameSize;
|
||||
|
||||
private String protocolWhitelist;
|
||||
|
||||
public FFmpegCommand() {
|
||||
this.args = new HashMap();
|
||||
this.x264Params = new HashMap();
|
||||
@ -82,6 +84,11 @@ public class FFmpegCommand {
|
||||
comm.add(probeSize);
|
||||
}
|
||||
|
||||
if(protocolWhitelist != null && !protocolWhitelist.isEmpty()) {
|
||||
comm.add("-protocol_whitelist");
|
||||
comm.add(protocolWhitelist);
|
||||
}
|
||||
|
||||
buildRtmpInput();
|
||||
|
||||
comm.add("-i");
|
||||
@ -323,6 +330,14 @@ public class FFmpegCommand {
|
||||
this.frameSize = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets protocol elements to be whitelisted
|
||||
* @param whitelist
|
||||
*/
|
||||
public void setProtocolWhitelist(String whitelist) {
|
||||
this.protocolWhitelist = whitelist;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add parameters for rtmp connections.
|
||||
* The order of parameters is the order they are added
|
||||
|
@ -4,6 +4,7 @@ akka {
|
||||
receive = on
|
||||
}
|
||||
}
|
||||
loggers = ["akka.event.slf4j.Slf4jLogger"]
|
||||
loglevel = INFO
|
||||
stdout-loglevel = "INFO"
|
||||
|
||||
|
@ -1,20 +1,20 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration>
|
||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>%date{ISO8601} %-5level %logger{36} %X{akkaSource} - %msg%n</pattern>
|
||||
</encoder>
|
||||
<layout class="ch.qos.logback.classic.PatternLayout">
|
||||
<Pattern>%d{"yyyy-MM-dd'T'HH:mm:ss.SSSXXX"} %-5level %logger{35} - %msg%n</Pattern>
|
||||
</layout>
|
||||
</appender>
|
||||
|
||||
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<File>logs/bbb-transcode.log</File>
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<FileNamePattern>logs/bbb-transcode.%d{yyyy-MM-dd}.log</FileNamePattern>
|
||||
<!-- keep 30 days worth of history -->
|
||||
<MaxHistory>5</MaxHistory>
|
||||
<!-- keep 14 days worth of history -->
|
||||
<MaxHistory>14</MaxHistory>
|
||||
</rollingPolicy>
|
||||
<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>
|
||||
</appender>
|
||||
|
||||
|
@ -5,7 +5,7 @@ import scala.concurrent.duration._
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
import akka.actor.ActorSystem
|
||||
import org.bigbluebutton.SystemConfiguration
|
||||
import org.bigbluebutton.common.converters.ToJsonEncoder
|
||||
//import org.bigbluebutton.common.converters.ToJsonEncoder
|
||||
|
||||
class RedisPublisher(val system: ActorSystem) extends SystemConfiguration {
|
||||
|
||||
@ -15,13 +15,13 @@ class RedisPublisher(val system: ActorSystem) extends SystemConfiguration {
|
||||
// CLIENT LIST on redis-cli
|
||||
redis.clientSetname("BbbTranscodeAkkaPub")
|
||||
|
||||
val encoder = new ToJsonEncoder()
|
||||
def sendPingMessage() {
|
||||
val json = encoder.encodePubSubPingMessage("BbbTranscode", System.currentTimeMillis())
|
||||
redis.publish("bigbluebutton:to-bbb-apps:system", json)
|
||||
}
|
||||
//val encoder = new ToJsonEncoder()
|
||||
//def sendPingMessage() {
|
||||
// val json = encoder.encodePubSubPingMessage("BbbTranscode", System.currentTimeMillis())
|
||||
// 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) {
|
||||
//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.
|
||||
//
|
||||
// Passed
|
||||
@ -169,6 +167,32 @@ public String getJoinMeetingURL(String username, String meetingID, String passwo
|
||||
// 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) {
|
||||
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_join = BigBlueButtonURL + "api/join?";
|
||||
@ -182,15 +206,15 @@ public String getJoinURL(String username, String meetingID, String record, Strin
|
||||
if ((xml != null) && !xml.equals("")) {
|
||||
xml_param = xml;
|
||||
}
|
||||
|
||||
|
||||
Random random = new Random();
|
||||
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
|
||||
// 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).
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
|
||||
String create_parameters = "name=" + urlEncode(meetingID)
|
||||
+ "&meetingID=" + urlEncode(meetingID) + welcome_param + voiceBridge_param
|
||||
+ "&attendeePW=ap&moderatorPW=mp"
|
||||
@ -207,12 +230,17 @@ public String getJoinURL(String username, String meetingID, String record, Strin
|
||||
+ "&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
|
||||
Document doc = null;
|
||||
try {
|
||||
String url = base_url_create + create_parameters
|
||||
+ "&checksum="
|
||||
+ checksum("create" + create_parameters + salt);
|
||||
+ checksum("create" + create_parameters + salt);
|
||||
doc = parseXml( postURL( url, xml_param ) );
|
||||
|
||||
} 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
|
||||
//
|
||||
//
|
||||
|
||||
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="
|
||||
+ checksum("join" + join_parameters + salt);
|
||||
}
|
||||
|
||||
|
||||
return doc.getElementsByTagName("messageKey").item(0).getTextContent()
|
||||
.trim()
|
||||
+ ": "
|
||||
+ ": "
|
||||
+ doc.getElementsByTagName("message").item(0).getTextContent()
|
||||
.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 style="text-align: right; ">Moderator Role:</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>
|
||||
@ -88,39 +88,22 @@ if (request.getParameterMap().isEmpty()) {
|
||||
<%
|
||||
} else if (request.getParameter("action").equals("create")) {
|
||||
|
||||
//
|
||||
// Got an action=create
|
||||
//
|
||||
|
||||
String username = request.getParameter("username");
|
||||
|
||||
// set defaults and overwrite them if custom values exist
|
||||
String meetingname = "Demo Meeting";
|
||||
if (request.getParameter("meetingname") != null) {
|
||||
meetingname = request.getParameter("meetingname");
|
||||
meetingname = request.getParameter("meetingname");
|
||||
}
|
||||
|
||||
String defaultModeratorPassword = "mp";
|
||||
String defaultAttendeePassword = "ap";
|
||||
String defaultPassword = defaultAttendeePassword;
|
||||
|
||||
boolean isModerator = false;
|
||||
Boolean isModerator = new Boolean(false);
|
||||
Boolean isHTML5 = new Boolean(true);
|
||||
Boolean isRecorded = new Boolean(true);
|
||||
if (request.getParameter("isModerator") != null) {
|
||||
isModerator = Boolean.parseBoolean(request.getParameter("isModerator"));
|
||||
defaultPassword = defaultModeratorPassword;
|
||||
}
|
||||
|
||||
String ip = BigBlueButtonURL.split("\\/bigbluebutton")[0];
|
||||
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);
|
||||
String joinURL = getJoinURLExtended(username, meetingname, isRecorded.toString(), null, null, null, isHTML5.toString(), isModerator.toString());
|
||||
|
||||
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="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://")) {
|
||||
joinURL = joinURL.replace("https", "bigbluebutton");
|
||||
joinURL = joinURL.replace("https", "bigbluebuttons");
|
||||
%>
|
||||
|
||||
<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 disconnect(ConnInfo connInfo);
|
||||
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)
|
||||
|
||||
|
||||
log.debug("*********** meetingManagerChannel = " + meetingManagerChannel)
|
||||
|
||||
private val msgFromClientEventBus = new MsgFromClientEventBus
|
||||
private val jsonMsgToAkkaAppsBus = new JsonMsgToAkkaAppsBus
|
||||
private val msgFromAkkaAppsEventBus = new MsgFromAkkaAppsEventBus
|
||||
private val msgToAkkaAppsEventBus = new MsgToAkkaAppsEventBus
|
||||
private val msgToRedisEventBus = new MsgToRedisEventBus
|
||||
private val msgToClientEventBus = new MsgToClientEventBus
|
||||
|
||||
private val redisPublisher = new RedisPublisher(system)
|
||||
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(
|
||||
MeetingManagerActor.props(msgToAkkaAppsEventBus, msgToClientEventBus), "meetingManagerActor")
|
||||
MeetingManagerActor.props(msgToRedisEventBus, msgToClientEventBus), "meetingManagerActor")
|
||||
|
||||
msgFromAkkaAppsEventBus.subscribe(meetingManagerActorRef, fromAkkaAppsChannel)
|
||||
msgFromClientEventBus.subscribe(meetingManagerActorRef, fromClientChannel)
|
||||
|
||||
private val receivedJsonMsgBus = new JsonMsgFromAkkaAppsBus
|
||||
|
||||
private val msgToAkkaAppsToJsonActor = system.actorOf(
|
||||
MsgToAkkaAppsToJsonActor.props(jsonMsgToAkkaAppsBus), "msgToAkkaAppsToJsonActor")
|
||||
private val msgToRedisActor = system.actorOf(
|
||||
MsgToRedisActor.props(msgSender), "msgToRedisActor")
|
||||
|
||||
msgToAkkaAppsEventBus.subscribe(msgToAkkaAppsToJsonActor, toAkkaAppsChannel)
|
||||
msgToRedisEventBus.subscribe(msgToRedisActor, toRedisChannel)
|
||||
|
||||
private val msgToClientJsonActor = system.actorOf(
|
||||
MsgToClientJsonActor.props(msgToClientGW), "msgToClientJsonActor")
|
||||
@ -79,11 +70,6 @@ class ClientGWApplication(val msgToClientGW: MsgToClientGW) extends SystemConfig
|
||||
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 = {
|
||||
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 = {
|
||||
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 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 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 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 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 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 {
|
||||
type Event = MsgToAkkaApps
|
||||
class MsgToRedisEventBus extends EventBus with LookupClassification {
|
||||
type Event = MsgToRedis
|
||||
type Classifier = String
|
||||
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 {
|
||||
|
||||
val channels = Seq(fromAkkaAppsRedisChannel, fromAkkaAppsWbRedisChannel, fromAkkaAppsChatRedisChannel, fromAkkaAppsPresRedisChannel)
|
||||
val channels = Seq(fromAkkaAppsRedisChannel, fromAkkaAppsWbRedisChannel, fromAkkaAppsChatRedisChannel, fromAkkaAppsPresRedisChannel, fromThirdPartyRedisChannel)
|
||||
val patterns = Seq("bigbluebutton:from-bbb-apps:*")
|
||||
|
||||
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,
|
||||
SetGuestPolicyCmdMsg.NAME,
|
||||
GuestsWaitingApprovedMsg.NAME,
|
||||
UserActivitySignCmdMsg.NAME,
|
||||
|
||||
// Webcams
|
||||
GetWebcamsOnlyForModeratorReqMsg.NAME,
|
||||
@ -50,6 +51,7 @@ object AllowedMessageNames {
|
||||
SendGroupChatMessageMsg.NAME,
|
||||
ClearPublicChatHistoryPubMsg.NAME,
|
||||
CreateGroupChatReqMsg.NAME,
|
||||
UserTypingPubMsg.NAME,
|
||||
|
||||
// Presentation Messages
|
||||
ResizeAndMovePagePubMsg.NAME,
|
||||
@ -106,6 +108,9 @@ object AllowedMessageNames {
|
||||
BreakoutRoomsListMsg.NAME,
|
||||
|
||||
// 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
|
||||
|
||||
import akka.actor.ActorContext
|
||||
import org.bigbluebutton.client.bus.{MsgToAkkaAppsEventBus, MsgToClientEventBus}
|
||||
import org.bigbluebutton.client.bus.{MsgToRedisEventBus, MsgToClientEventBus}
|
||||
|
||||
object Meeting {
|
||||
def apply(meetingId: String, msgToAkkaAppsEventBus: MsgToAkkaAppsEventBus,
|
||||
def apply(meetingId: String, msgToRedisEventBus: MsgToRedisEventBus,
|
||||
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) {
|
||||
|
||||
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}
|
||||
|
||||
object MeetingActor {
|
||||
def props(meetingId: String, msgToAkkaAppsEventBus: MsgToAkkaAppsEventBus,
|
||||
def props(meetingId: String, msgToRedisEventBus: MsgToRedisEventBus,
|
||||
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)
|
||||
extends Actor with ActorLogging
|
||||
with SystemConfiguration{
|
||||
@ -27,7 +27,7 @@ class MeetingActor(val meetingId: String, msgToAkkaAppsEventBus: MsgToAkkaAppsEv
|
||||
}
|
||||
|
||||
private def createUser(id: String): User = {
|
||||
User(id, msgToAkkaAppsEventBus, meetingId, msgToClientEventBus)
|
||||
User(id, msgToRedisEventBus, meetingId, msgToClientEventBus)
|
||||
}
|
||||
|
||||
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 {
|
||||
def props(msgToAkkaAppsEventBus: MsgToAkkaAppsEventBus,
|
||||
def props(msgToAkkaAppsEventBus: MsgToRedisEventBus,
|
||||
msgToClientEventBus: MsgToClientEventBus): Props =
|
||||
Props(classOf[MeetingManagerActor], msgToAkkaAppsEventBus, msgToClientEventBus)
|
||||
}
|
||||
|
||||
class MeetingManagerActor(msgToAkkaAppsEventBus: MsgToAkkaAppsEventBus,
|
||||
class MeetingManagerActor(msgToRedisEventBus: MsgToRedisEventBus,
|
||||
msgToClientEventBus: MsgToClientEventBus)
|
||||
extends Actor with ActorLogging {
|
||||
|
||||
@ -26,7 +26,7 @@ class MeetingManagerActor(msgToAkkaAppsEventBus: MsgToAkkaAppsEventBus,
|
||||
}
|
||||
|
||||
def createMeeting(meetingId: String): Meeting = {
|
||||
Meeting(meetingId, msgToAkkaAppsEventBus, msgToClientEventBus)
|
||||
Meeting(meetingId, msgToRedisEventBus, msgToClientEventBus)
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
import akka.actor.ActorContext
|
||||
import org.bigbluebutton.client.bus.{MsgToAkkaAppsEventBus, MsgToClientEventBus}
|
||||
import org.bigbluebutton.client.bus.{MsgToRedisEventBus, MsgToClientEventBus}
|
||||
|
||||
object User {
|
||||
def apply(userId: String,
|
||||
msgToAkkaAppsEventBus: MsgToAkkaAppsEventBus,
|
||||
msgToRedisEventBus: MsgToRedisEventBus,
|
||||
meetingId: String,
|
||||
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,
|
||||
msgToAkkaAppsEventBus: MsgToAkkaAppsEventBus,
|
||||
msgToRedisEventBus: MsgToRedisEventBus,
|
||||
meetingId: String,
|
||||
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)
|
||||
}
|
||||
|
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 {
|
||||
def props(userId: String,
|
||||
msgToAkkaAppsEventBus: MsgToAkkaAppsEventBus,
|
||||
msgToRedisEventBus: MsgToRedisEventBus,
|
||||
meetingId: String,
|
||||
msgToClientEventBus: MsgToClientEventBus): Props =
|
||||
Props(classOf[UserActor], userId, msgToAkkaAppsEventBus, meetingId, msgToClientEventBus)
|
||||
Props(classOf[UserActor], userId, msgToRedisEventBus, meetingId, msgToClientEventBus)
|
||||
}
|
||||
|
||||
class UserActor(val userId: String,
|
||||
msgToAkkaAppsEventBus: MsgToAkkaAppsEventBus,
|
||||
msgToRedisEventBus: MsgToRedisEventBus,
|
||||
meetingId: String,
|
||||
msgToClientEventBus: MsgToClientEventBus)
|
||||
extends Actor with ActorLogging with SystemConfiguration {
|
||||
@ -133,8 +133,8 @@ class UserActor(val userId: String,
|
||||
for {
|
||||
jsonNode <- convertToJsonNode(json)
|
||||
} yield {
|
||||
val akkaMsg = BbbCommonEnvJsNodeMsg(envelope, jsonNode)
|
||||
msgToAkkaAppsEventBus.publish(MsgToAkkaApps(toAkkaAppsChannel, akkaMsg))
|
||||
val jsNodeMsg = BbbCommonEnvJsNodeMsg(envelope, jsonNode)
|
||||
msgToRedisEventBus.publish(MsgToRedis(toRedisChannel, jsNodeMsg))
|
||||
}
|
||||
}
|
||||
case None =>
|
||||
|
@ -1,22 +1,22 @@
|
||||
/**
|
||||
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
|
||||
*
|
||||
* 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
|
||||
* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
|
||||
*
|
||||
* 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
|
||||
* 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.clientcheck.service
|
||||
{
|
||||
import flash.events.AsyncErrorEvent;
|
||||
@ -43,6 +43,7 @@ package org.bigbluebutton.clientcheck.service
|
||||
private static var RECORD_MOCK:Boolean=false;
|
||||
private static var EXTERNAL_USER_ID_MOCK:String="123456";
|
||||
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;
|
||||
|
||||
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.
|
||||
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;
|
||||
}
|
||||
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 DurationProps(duration: Int, createdTime: Long, createdDate: String, maxInactivityTimeoutMinutes: Int,
|
||||
warnMinutesBeforeMax: Int, meetingExpireIfNoUserJoinedInMinutes: Int,
|
||||
meetingExpireWhenLastUserLeftInMinutes: Int)
|
||||
case class DurationProps(duration: Int, createdTime: Long, createdDate: String,
|
||||
maxInactivityTimeoutMinutes: Int, warnMinutesBeforeMax: Int,
|
||||
meetingExpireIfNoUserJoinedInMinutes: Int, meetingExpireWhenLastUserLeftInMinutes: Int,
|
||||
userInactivityInspectTimerInMinutes: Int, userInactivityThresholdInMinutes: Int, userActivitySignResponseDelayInMinutes: Int)
|
||||
|
||||
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)
|
||||
|
||||
|
@ -8,13 +8,13 @@ package org.bigbluebutton.common2.msgs
|
||||
object BreakoutRoomJoinURLEvtMsg { val NAME = "BreakoutRoomJoinURLEvtMsg" }
|
||||
case class BreakoutRoomJoinURLEvtMsg(header: BbbClientMsgHeader, body: BreakoutRoomJoinURLEvtMsgBody) extends BbbCoreMsg
|
||||
case class BreakoutRoomJoinURLEvtMsgBody(parentId: String, breakoutId: String, externalId: String,
|
||||
userId: String, redirectJoinURL: String, noRedirectJoinURL: String)
|
||||
userId: String, redirectJoinURL: String, redirectToHtml5JoinURL: String)
|
||||
|
||||
// Outgoing messages
|
||||
object BreakoutRoomsListEvtMsg { val NAME = "BreakoutRoomsListEvtMsg" }
|
||||
case class BreakoutRoomsListEvtMsg(header: BbbClientMsgHeader, body: BreakoutRoomsListEvtMsgBody) extends BbbCoreMsg
|
||||
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" }
|
||||
case class BreakoutRoomsListMsg(header: BbbClientMsgHeader, body: BreakoutRoomsListMsgBody) extends StandardMsg
|
||||
@ -39,7 +39,7 @@ package org.bigbluebutton.common2.msgs
|
||||
case class CreateBreakoutRoomSysCmdMsg(header: BbbCoreBaseHeader,
|
||||
body: CreateBreakoutRoomSysCmdMsgBody) extends BbbCoreMsg
|
||||
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,
|
||||
sourcePresentationId: String, sourcePresentationSlide: Int, record: Boolean)
|
||||
|
||||
@ -49,7 +49,7 @@ package org.bigbluebutton.common2.msgs
|
||||
object CreateBreakoutRoomsCmdMsg { val NAME = "CreateBreakoutRoomsCmdMsg" }
|
||||
case class CreateBreakoutRoomsCmdMsg(header: BbbClientMsgHeader, body: CreateBreakoutRoomsCmdMsgBody) extends StandardMsg
|
||||
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
|
||||
object EndAllBreakoutRoomsMsg { val NAME = "EndAllBreakoutRoomsMsg" }
|
||||
@ -69,7 +69,7 @@ package org.bigbluebutton.common2.msgs
|
||||
object RequestBreakoutJoinURLRespMsg { val NAME = "RequestBreakoutJoinURLRespMsg" }
|
||||
case class RequestBreakoutJoinURLRespMsg(header: BbbClientMsgHeader, body: RequestBreakoutJoinURLRespMsgBody) extends BbbCoreMsg
|
||||
case class RequestBreakoutJoinURLRespMsgBody(parentId: String, breakoutId: String,
|
||||
userId: String, redirectJoinURL: String, noRedirectJoinURL: String)
|
||||
userId: String, redirectJoinURL: String, redirectToHtml5JoinURL: String)
|
||||
|
||||
|
||||
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 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])
|
||||
|
||||
|
||||
|
@ -1,39 +1,42 @@
|
||||
package org.bigbluebutton.common2.msgs
|
||||
|
||||
|
||||
/* In Messages */
|
||||
object GetChatHistoryReqMsg { val NAME = "GetChatHistoryReqMsg"}
|
||||
object GetChatHistoryReqMsg { val NAME = "GetChatHistoryReqMsg" }
|
||||
case class GetChatHistoryReqMsg(header: BbbClientMsgHeader, body: GetChatHistoryReqMsgBody) extends StandardMsg
|
||||
case class GetChatHistoryReqMsgBody()
|
||||
|
||||
object SendPublicMessagePubMsg { val NAME = "SendPublicMessagePubMsg"}
|
||||
object SendPublicMessagePubMsg { val NAME = "SendPublicMessagePubMsg" }
|
||||
case class SendPublicMessagePubMsg(header: BbbClientMsgHeader, body: SendPublicMessagePubMsgBody) extends StandardMsg
|
||||
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 SendPrivateMessagePubMsgBody(message: ChatMessageVO)
|
||||
|
||||
object ClearPublicChatHistoryPubMsg { val NAME = "ClearPublicChatHistoryPubMsg"}
|
||||
object ClearPublicChatHistoryPubMsg { val NAME = "ClearPublicChatHistoryPubMsg" }
|
||||
case class ClearPublicChatHistoryPubMsg(header: BbbClientMsgHeader, body: ClearPublicChatHistoryPubMsgBody) extends StandardMsg
|
||||
case class ClearPublicChatHistoryPubMsgBody(chatId: String)
|
||||
|
||||
/* Out Messages */
|
||||
object GetChatHistoryRespMsg { val NAME = "GetChatHistoryRespMsg"}
|
||||
object GetChatHistoryRespMsg { val NAME = "GetChatHistoryRespMsg" }
|
||||
case class GetChatHistoryRespMsg(header: BbbClientMsgHeader, body: GetChatHistoryRespMsgBody) extends StandardMsg
|
||||
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 SendPublicMessageEvtMsgBody(message: ChatMessageVO)
|
||||
|
||||
object SendPrivateMessageEvtMsg { val NAME = "SendPrivateMessageEvtMsg"}
|
||||
object SendPrivateMessageEvtMsg { val NAME = "SendPrivateMessageEvtMsg" }
|
||||
case class SendPrivateMessageEvtMsg(header: BbbClientMsgHeader, body: SendPrivateMessageEvtMsgBody) extends StandardMsg
|
||||
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 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,
|
||||
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