Implements endWhenNoModerator
This commit is contained in:
parent
62b24b8c19
commit
00240c925e
@ -32,4 +32,5 @@ object MeetingEndReason {
|
||||
val BREAKOUT_ENDED_EXCEEDING_DURATION = "BREAKOUT_ENDED_EXCEEDING_DURATION"
|
||||
val BREAKOUT_ENDED_BY_MOD = "BREAKOUT_ENDED_BY_MOD"
|
||||
val ENDED_DUE_TO_NO_AUTHED_USER = "ENDED_DUE_TO_NO_AUTHED_USER"
|
||||
val ENDED_DUE_TO_NO_MODERATOR = "ENDED_DUE_TO_NO_MODERATOR"
|
||||
}
|
||||
|
@ -3,14 +3,18 @@ package org.bigbluebutton.core.domain
|
||||
case class MeetingExpiryTracker(
|
||||
startedOnInMs: Long,
|
||||
userHasJoined: Boolean,
|
||||
moderatorHasJoined: Boolean,
|
||||
isBreakout: Boolean,
|
||||
lastUserLeftOnInMs: Option[Long],
|
||||
lastModeratorLeftOnInMs: Long,
|
||||
durationInMs: Long,
|
||||
meetingExpireIfNoUserJoinedInMs: Long,
|
||||
meetingExpireWhenLastUserLeftInMs: Long,
|
||||
userInactivityInspectTimerInMs: Long,
|
||||
userInactivityThresholdInMs: Long,
|
||||
userActivitySignResponseDelayInMs: Long
|
||||
userActivitySignResponseDelayInMs: Long,
|
||||
endWhenNoModerator: Boolean,
|
||||
endWhenNoModeratorDelayInMs: Long
|
||||
) {
|
||||
def setUserHasJoined(): MeetingExpiryTracker = {
|
||||
if (!userHasJoined) {
|
||||
@ -24,6 +28,18 @@ case class MeetingExpiryTracker(
|
||||
copy(lastUserLeftOnInMs = Some(timestampInMs))
|
||||
}
|
||||
|
||||
def setModeratorHasJoined(): MeetingExpiryTracker = {
|
||||
if (!moderatorHasJoined) {
|
||||
copy(moderatorHasJoined = true, lastModeratorLeftOnInMs = 0)
|
||||
} else {
|
||||
copy(lastModeratorLeftOnInMs = 0)
|
||||
}
|
||||
}
|
||||
|
||||
def setLastModeratorLeftOn(timestampInMs: Long): MeetingExpiryTracker = {
|
||||
copy(lastModeratorLeftOnInMs = timestampInMs)
|
||||
}
|
||||
|
||||
def hasMeetingExpiredAfterLastUserLeft(timestampInMs: Long): Boolean = {
|
||||
val expire = for {
|
||||
lastUserLeftOn <- lastUserLeftOnInMs
|
||||
|
@ -55,6 +55,10 @@ object Users2x {
|
||||
users.toVector.length
|
||||
}
|
||||
|
||||
def numActiveModerators(users: Users2x): Int = {
|
||||
users.toVector.filter(u => u.role == Roles.MODERATOR_ROLE && !u.userLeftFlag.left).length
|
||||
}
|
||||
|
||||
def findNotPresenters(users: Users2x): Vector[UserState] = {
|
||||
users.toVector.filter(u => !u.presenter)
|
||||
}
|
||||
|
@ -135,14 +135,18 @@ class MeetingActor(
|
||||
val expiryTracker = new MeetingExpiryTracker(
|
||||
startedOnInMs = TimeUtil.timeNowInMs(),
|
||||
userHasJoined = false,
|
||||
moderatorHasJoined = false,
|
||||
isBreakout = props.meetingProp.isBreakout,
|
||||
lastUserLeftOnInMs = None,
|
||||
lastModeratorLeftOnInMs = 0,
|
||||
durationInMs = TimeUtil.minutesToMillis(props.durationProps.duration),
|
||||
meetingExpireIfNoUserJoinedInMs = TimeUtil.minutesToMillis(props.durationProps.meetingExpireIfNoUserJoinedInMinutes),
|
||||
meetingExpireWhenLastUserLeftInMs = TimeUtil.minutesToMillis(props.durationProps.meetingExpireWhenLastUserLeftInMinutes),
|
||||
userInactivityInspectTimerInMs = TimeUtil.minutesToMillis(props.durationProps.userInactivityInspectTimerInMinutes),
|
||||
userInactivityThresholdInMs = TimeUtil.minutesToMillis(props.durationProps.userInactivityThresholdInMinutes),
|
||||
userActivitySignResponseDelayInMs = TimeUtil.minutesToMillis(props.durationProps.userActivitySignResponseDelayInMinutes)
|
||||
userActivitySignResponseDelayInMs = TimeUtil.minutesToMillis(props.durationProps.userActivitySignResponseDelayInMinutes),
|
||||
endWhenNoModerator = props.durationProps.endWhenNoModerator,
|
||||
endWhenNoModeratorDelayInMs = TimeUtil.minutesToMillis(props.durationProps.endWhenNoModeratorDelayInMinutes)
|
||||
)
|
||||
|
||||
val recordingTracker = new MeetingRecordingTracker(startedOnInMs = 0L, previousDurationInMs = 0L, currentDurationInMs = 0L)
|
||||
@ -298,6 +302,23 @@ class MeetingActor(
|
||||
}
|
||||
}
|
||||
|
||||
private def updateModeratorsPresence() {
|
||||
if (Users2x.numActiveModerators(liveMeeting.users2x) > 0) {
|
||||
if (state.expiryTracker.moderatorHasJoined == false ||
|
||||
state.expiryTracker.lastModeratorLeftOnInMs != 0) {
|
||||
log.info("A moderator has joined. Setting setModeratorHasJoined(). meetingId=" + props.meetingProp.intId)
|
||||
val tracker = state.expiryTracker.setModeratorHasJoined()
|
||||
state = state.update(tracker)
|
||||
}
|
||||
} else {
|
||||
if (state.expiryTracker.moderatorHasJoined == true) {
|
||||
log.info("All moderators have left. Setting setLastModeratorLeftOn(). meetingId=" + props.meetingProp.intId)
|
||||
val tracker = state.expiryTracker.setLastModeratorLeftOn(TimeUtil.timeNowInMs())
|
||||
state = state.update(tracker)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private def updateUserLastInactivityInspect(userId: String) {
|
||||
for {
|
||||
user <- Users2x.findWithIntId(liveMeeting.users2x, userId)
|
||||
@ -317,20 +338,26 @@ class MeetingActor(
|
||||
private def handleMessageThatAffectsInactivity(msg: BbbCommonEnvCoreMsg): Unit = {
|
||||
|
||||
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: UserJoinMeetingAfterReconnectReqMsg => state = handleUserJoinMeetingAfterReconnectReqMsg(m, state)
|
||||
case m: UserLeaveReqMsg => state = handleUserLeaveReqMsg(m, state)
|
||||
case m: UserBroadcastCamStartMsg => handleUserBroadcastCamStartMsg(m)
|
||||
case m: UserBroadcastCamStopMsg => handleUserBroadcastCamStopMsg(m)
|
||||
case m: GetCamBroadcastPermissionReqMsg => handleGetCamBroadcastPermissionReqMsg(m)
|
||||
case m: GetCamSubscribePermissionReqMsg => handleGetCamSubscribePermissionReqMsg(m)
|
||||
case m: ValidateAuthTokenReqMsg => state = usersApp.handleValidateAuthTokenReqMsg(m, state)
|
||||
case m: UserJoinMeetingReqMsg =>
|
||||
state = handleUserJoinMeetingReqMsg(m, state)
|
||||
updateModeratorsPresence()
|
||||
case m: UserJoinMeetingAfterReconnectReqMsg =>
|
||||
state = handleUserJoinMeetingAfterReconnectReqMsg(m, state)
|
||||
updateModeratorsPresence()
|
||||
case m: UserLeaveReqMsg =>
|
||||
state = handleUserLeaveReqMsg(m, state)
|
||||
updateModeratorsPresence()
|
||||
case m: UserBroadcastCamStartMsg => handleUserBroadcastCamStartMsg(m)
|
||||
case m: UserBroadcastCamStopMsg => handleUserBroadcastCamStopMsg(m)
|
||||
case m: GetCamBroadcastPermissionReqMsg => handleGetCamBroadcastPermissionReqMsg(m)
|
||||
case m: GetCamSubscribePermissionReqMsg => handleGetCamSubscribePermissionReqMsg(m)
|
||||
|
||||
case m: UserJoinedVoiceConfEvtMsg => handleUserJoinedVoiceConfEvtMsg(m)
|
||||
case m: LogoutAndEndMeetingCmdMsg => usersApp.handleLogoutAndEndMeetingCmdMsg(m, state)
|
||||
case m: UserJoinedVoiceConfEvtMsg => handleUserJoinedVoiceConfEvtMsg(m)
|
||||
case m: LogoutAndEndMeetingCmdMsg => usersApp.handleLogoutAndEndMeetingCmdMsg(m, state)
|
||||
case m: SetRecordingStatusCmdMsg =>
|
||||
state = usersApp.handleSetRecordingStatusCmdMsg(m, state)
|
||||
updateUserLastActivity(m.body.setBy)
|
||||
@ -354,6 +381,7 @@ class MeetingActor(
|
||||
case m: ChangeUserRoleCmdMsg =>
|
||||
usersApp.handleChangeUserRoleCmdMsg(m)
|
||||
updateUserLastActivity(m.body.changedBy)
|
||||
updateModeratorsPresence()
|
||||
|
||||
// Whiteboard
|
||||
case m: SendCursorPositionPubMsg => wbApp.handle(m, liveMeeting, msgBus)
|
||||
@ -596,7 +624,8 @@ class MeetingActor(
|
||||
|
||||
processUserInactivityAudit()
|
||||
flagRegisteredUsersWhoHasNotJoined()
|
||||
checkIfNeetToEndMeetingWhenNoAuthedUsers(liveMeeting)
|
||||
checkIfNeedToEndMeetingWhenNoAuthedUsers(liveMeeting)
|
||||
checkIfNeedToEndMeetingWhenNoNoModerators(liveMeeting)
|
||||
}
|
||||
|
||||
def checkVoiceConfUsersStatus(): Unit = {
|
||||
@ -663,7 +692,7 @@ class MeetingActor(
|
||||
|
||||
}
|
||||
|
||||
private def checkIfNeetToEndMeetingWhenNoAuthedUsers(liveMeeting: LiveMeeting): Unit = {
|
||||
private def checkIfNeedToEndMeetingWhenNoAuthedUsers(liveMeeting: LiveMeeting): Unit = {
|
||||
val authUserJoined = MeetingStatus2x.hasAuthedUserJoined(liveMeeting.status)
|
||||
|
||||
if (endMeetingWhenNoMoreAuthedUsers &&
|
||||
@ -684,6 +713,28 @@ class MeetingActor(
|
||||
}
|
||||
}
|
||||
|
||||
private def checkIfNeedToEndMeetingWhenNoNoModerators(liveMeeting: LiveMeeting): Unit = {
|
||||
if (state.expiryTracker.endWhenNoModerator &&
|
||||
!liveMeeting.props.meetingProp.isBreakout &&
|
||||
state.expiryTracker.moderatorHasJoined &&
|
||||
state.expiryTracker.lastModeratorLeftOnInMs != 0 &&
|
||||
//Check if has moderator with leftFlag
|
||||
Users2x.findModerator(liveMeeting.users2x).toVector.length == 0) {
|
||||
val hasModeratorLeftRecently = (TimeUtil.timeNowInMs() - state.expiryTracker.endWhenNoModeratorDelayInMs) < state.expiryTracker.lastModeratorLeftOnInMs
|
||||
if (!hasModeratorLeftRecently) {
|
||||
log.info("Meeting will end due option endWhenNoModerator is enabled and all moderators have left the meeting. meetingId=" + props.meetingProp.intId)
|
||||
sendEndMeetingDueToExpiry(
|
||||
MeetingEndReason.ENDED_DUE_TO_NO_MODERATOR,
|
||||
eventBus, outGW, liveMeeting,
|
||||
"system"
|
||||
)
|
||||
} else {
|
||||
val msToEndMeeting = state.expiryTracker.lastModeratorLeftOnInMs - (TimeUtil.timeNowInMs() - state.expiryTracker.endWhenNoModeratorDelayInMs)
|
||||
log.info("All moderators have left. Meeting will end in " + TimeUtil.millisToSeconds(msToEndMeeting) + " seconds. meetingId=" + props.meetingProp.intId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def handleExtendMeetingDuration(msg: ExtendMeetingDuration) {
|
||||
|
||||
}
|
||||
|
@ -4,7 +4,9 @@ case class ConfigProps(defaultConfigToken: String, config: String)
|
||||
|
||||
case class DurationProps(duration: Int, createdTime: Long, createdDate: String,
|
||||
meetingExpireIfNoUserJoinedInMinutes: Int, meetingExpireWhenLastUserLeftInMinutes: Int,
|
||||
userInactivityInspectTimerInMinutes: Int, userInactivityThresholdInMinutes: Int, userActivitySignResponseDelayInMinutes: Int)
|
||||
userInactivityInspectTimerInMinutes: Int, userInactivityThresholdInMinutes: Int,
|
||||
userActivitySignResponseDelayInMinutes: Int,
|
||||
endWhenNoModerator: Boolean, endWhenNoModeratorDelayInMinutes: Int)
|
||||
|
||||
case class MeetingProp(name: String, extId: String, intId: String, isBreakout: Boolean)
|
||||
|
||||
|
@ -17,6 +17,8 @@ trait TestFixtures {
|
||||
val userInactivityInspectTimerInMinutes = 60
|
||||
val userInactivityThresholdInMinutes = 10
|
||||
val userActivitySignResponseDelayInMinutes = 5
|
||||
val endWhenNoModerator = false
|
||||
val endWhenNoModeratorDelayInMinutes = 1
|
||||
|
||||
val autoStartRecording = false
|
||||
val allowStartStopRecording = false
|
||||
|
@ -432,7 +432,8 @@ public class MeetingService implements MessageListener {
|
||||
m.getDialNumber(), m.getMaxUsers(),
|
||||
m.getMeetingExpireIfNoUserJoinedInMinutes(), m.getmeetingExpireWhenLastUserLeftInMinutes(),
|
||||
m.getUserInactivityInspectTimerInMinutes(), m.getUserInactivityThresholdInMinutes(),
|
||||
m.getUserActivitySignResponseDelayInMinutes(), m.getMuteOnStart(), m.getAllowModsToUnmuteUsers(), m.getMeetingKeepEvents(),
|
||||
m.getUserActivitySignResponseDelayInMinutes(), m.getEndWhenNoModerator(), m.getEndWhenNoModeratorDelayInMinutes(),
|
||||
m.getMuteOnStart(), m.getAllowModsToUnmuteUsers(), m.getMeetingKeepEvents(),
|
||||
m.breakoutRoomsParams,
|
||||
m.lockSettingsParams, m.getHtml5InstanceId());
|
||||
}
|
||||
|
@ -115,6 +115,7 @@ public class ParamsProcessorUtil {
|
||||
private Integer userActivitySignResponseDelayInMinutes = 5;
|
||||
private Boolean defaultAllowDuplicateExtUserid = true;
|
||||
private Boolean defaultEndWhenNoModerator = false;
|
||||
private Integer defaultEndWhenNoModeratorDelayInMinutes = 1;
|
||||
private Integer defaultHtml5InstanceId = 1;
|
||||
|
||||
private String formatConfNum(String s) {
|
||||
@ -523,6 +524,8 @@ public class ParamsProcessorUtil {
|
||||
meeting.setUserActivitySignResponseDelayInMinutes(userActivitySignResponseDelayInMinutes);
|
||||
meeting.setUserInactivityThresholdInMinutes(userInactivityThresholdInMinutes);
|
||||
// meeting.setHtml5InstanceId(html5InstanceId);
|
||||
meeting.setEndWhenNoModerator(defaultEndWhenNoModerator);
|
||||
meeting.setEndWhenNoModeratorDelayInMinutes(defaultEndWhenNoModeratorDelayInMinutes);
|
||||
|
||||
// Add extra parameters for breakout room
|
||||
if (isBreakout) {
|
||||
@ -1156,5 +1159,8 @@ public class ParamsProcessorUtil {
|
||||
this.defaultEndWhenNoModerator = val;
|
||||
}
|
||||
|
||||
public void setEndWhenNoModeratorDelayInMinutes(Integer value) {
|
||||
this.defaultEndWhenNoModeratorDelayInMinutes = value;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -91,6 +91,8 @@ public class Meeting {
|
||||
private Integer userInactivityInspectTimerInMinutes = 120;
|
||||
private Integer userInactivityThresholdInMinutes = 30;
|
||||
private Integer userActivitySignResponseDelayInMinutes = 5;
|
||||
private Boolean endWhenNoModerator = false;
|
||||
private Integer endWhenNoModeratorDelayInMinutes = 1;
|
||||
|
||||
public final BreakoutRoomsParams breakoutRoomsParams;
|
||||
public final LockSettingsParams lockSettingsParams;
|
||||
@ -99,8 +101,6 @@ public class Meeting {
|
||||
|
||||
private String meetingEndedCallbackURL = "";
|
||||
|
||||
public final Boolean endWhenNoModerator;
|
||||
|
||||
private Integer html5InstanceId;
|
||||
|
||||
public Meeting(Meeting.Builder builder) {
|
||||
@ -134,6 +134,7 @@ public class Meeting {
|
||||
lockSettingsParams = builder.lockSettingsParams;
|
||||
allowDuplicateExtUserid = builder.allowDuplicateExtUserid;
|
||||
endWhenNoModerator = builder.endWhenNoModerator;
|
||||
endWhenNoModeratorDelayInMinutes = builder.endWhenNoModeratorDelayInMinutes;
|
||||
html5InstanceId = builder.html5InstanceId;
|
||||
|
||||
/*
|
||||
@ -660,6 +661,22 @@ public class Meeting {
|
||||
this.userActivitySignResponseDelayInMinutes = userActivitySignResponseDelayInMinutes;
|
||||
}
|
||||
|
||||
public Boolean getEndWhenNoModerator() {
|
||||
return endWhenNoModerator;
|
||||
}
|
||||
|
||||
public void setEndWhenNoModerator(Boolean endWhenNoModerator) {
|
||||
this.endWhenNoModerator = endWhenNoModerator;
|
||||
}
|
||||
|
||||
public Integer getEndWhenNoModeratorDelayInMinutes() {
|
||||
return endWhenNoModeratorDelayInMinutes;
|
||||
}
|
||||
|
||||
public void setEndWhenNoModeratorDelayInMinutes(Integer endWhenNoModeratorDelayInMinutes) {
|
||||
this.endWhenNoModeratorDelayInMinutes = endWhenNoModeratorDelayInMinutes;
|
||||
}
|
||||
|
||||
public String getMeetingEndedCallbackURL() {
|
||||
return meetingEndedCallbackURL;
|
||||
}
|
||||
@ -742,6 +759,7 @@ public class Meeting {
|
||||
private LockSettingsParams lockSettingsParams;
|
||||
private Boolean allowDuplicateExtUserid;
|
||||
private Boolean endWhenNoModerator;
|
||||
private Integer endWhenNoModeratorDelayInMinutes;
|
||||
private int html5InstanceId;
|
||||
|
||||
public Builder(String externalId, String internalId, long createTime) {
|
||||
@ -885,6 +903,11 @@ public class Meeting {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder withEndWhenNoModeratorDelayInMinutes(Integer endWhenNoModeratorDelayInMinutes) {
|
||||
this.endWhenNoModeratorDelayInMinutes = endWhenNoModeratorDelayInMinutes;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder withHTML5InstanceId(int instanceId) {
|
||||
html5InstanceId = instanceId;
|
||||
return this;
|
||||
|
@ -26,6 +26,8 @@ public interface IBbbWebApiGWApp {
|
||||
Integer userInactivityInspectTimerInMinutes,
|
||||
Integer userInactivityThresholdInMinutes,
|
||||
Integer userActivitySignResponseDelayInMinutes,
|
||||
Boolean endWhenNoModerator,
|
||||
Integer endWhenNoModeratorDelayInMinutes,
|
||||
Boolean muteOnStart,
|
||||
Boolean allowModsToUnmuteUsers,
|
||||
Boolean keepEvents,
|
||||
|
@ -135,6 +135,8 @@ class BbbWebApiGWApp(
|
||||
userInactivityInspectTimerInMinutes: java.lang.Integer,
|
||||
userInactivityThresholdInMinutes: java.lang.Integer,
|
||||
userActivitySignResponseDelayInMinutes: java.lang.Integer,
|
||||
endWhenNoModerator: java.lang.Boolean,
|
||||
endWhenNoModeratorDelayInMinutes: java.lang.Integer,
|
||||
muteOnStart: java.lang.Boolean,
|
||||
allowModsToUnmuteUsers: java.lang.Boolean,
|
||||
keepEvents: java.lang.Boolean,
|
||||
@ -151,7 +153,9 @@ class BbbWebApiGWApp(
|
||||
meetingExpireWhenLastUserLeftInMinutes = meetingExpireWhenLastUserLeftInMinutes.intValue(),
|
||||
userInactivityInspectTimerInMinutes = userInactivityInspectTimerInMinutes.intValue(),
|
||||
userInactivityThresholdInMinutes = userInactivityThresholdInMinutes.intValue(),
|
||||
userActivitySignResponseDelayInMinutes = userActivitySignResponseDelayInMinutes.intValue()
|
||||
userActivitySignResponseDelayInMinutes = userActivitySignResponseDelayInMinutes.intValue(),
|
||||
endWhenNoModerator = endWhenNoModerator.booleanValue(),
|
||||
endWhenNoModeratorDelayInMinutes.intValue()
|
||||
)
|
||||
|
||||
val password = PasswordProp(moderatorPass = moderatorPass, viewerPass = viewerPass)
|
||||
|
@ -47,6 +47,8 @@ export default function addMeeting(meeting) {
|
||||
userInactivityInspectTimerInMinutes: Number,
|
||||
userInactivityThresholdInMinutes: Number,
|
||||
userActivitySignResponseDelayInMinutes: Number,
|
||||
endWhenNoModerator: Boolean,
|
||||
endWhenNoModeratorDelayInMinutes: Number,
|
||||
timeRemaining: Number,
|
||||
},
|
||||
welcomeProp: {
|
||||
|
@ -387,3 +387,6 @@ defaultTextTrackUrl=${bigbluebutton.web.serverURL}/bigbluebutton
|
||||
# Needed for classes where teacher gets disconnected and can't get back in. Prevents
|
||||
# students from running amok.
|
||||
endWhenNoModerator=false
|
||||
|
||||
# Number of minutes to wait for moderator rejoin before end meeting (if `endWhenNoModerator` enabled)
|
||||
endWhenNoModeratorDelayInMinutes=1
|
||||
|
@ -164,6 +164,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
<property name="lockSettingsLockOnJoinConfigurable" value="${lockSettingsLockOnJoinConfigurable}"/>
|
||||
<property name="allowDuplicateExtUserid" value="${allowDuplicateExtUserid}"/>
|
||||
<property name="endWhenNoModerator" value="${endWhenNoModerator}"/>
|
||||
<property name="endWhenNoModeratorDelayInMinutes" value="${endWhenNoModeratorDelayInMinutes}"/>
|
||||
<property name="defaultKeepEvents" value="${defaultKeepEvents}"/>
|
||||
</bean>
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user