Feat(meeting): minimise sequential user pick (#13831)
Updating random user picker to reduce the probability that the exact same viewers would be selected first in successive random-pick runs
This commit is contained in:
parent
1a89f66c0e
commit
0bc62f66f4
@ -50,6 +50,8 @@ trait SystemConfiguration {
|
|||||||
lazy val endMeetingWhenNoMoreAuthedUsers = Try(config.getBoolean("apps.endMeetingWhenNoMoreAuthedUsers")).getOrElse(false)
|
lazy val endMeetingWhenNoMoreAuthedUsers = Try(config.getBoolean("apps.endMeetingWhenNoMoreAuthedUsers")).getOrElse(false)
|
||||||
lazy val endMeetingWhenNoMoreAuthedUsersAfterMinutes = Try(config.getInt("apps.endMeetingWhenNoMoreAuthedUsersAfterMinutes")).getOrElse(2)
|
lazy val endMeetingWhenNoMoreAuthedUsersAfterMinutes = Try(config.getInt("apps.endMeetingWhenNoMoreAuthedUsersAfterMinutes")).getOrElse(2)
|
||||||
|
|
||||||
|
lazy val reduceDuplicatedPick = Try(config.getBoolean("apps.reduceDuplicatedPick")).getOrElse(false)
|
||||||
|
|
||||||
// Redis server configuration
|
// Redis server configuration
|
||||||
lazy val redisHost = Try(config.getString("redis.host")).getOrElse("127.0.0.1")
|
lazy val redisHost = Try(config.getString("redis.host")).getOrElse("127.0.0.1")
|
||||||
lazy val redisPort = Try(config.getInt("redis.port")).getOrElse(6379)
|
lazy val redisPort = Try(config.getInt("redis.port")).getOrElse(6379)
|
||||||
|
@ -5,6 +5,7 @@ import org.bigbluebutton.core.running.{ LiveMeeting, OutMsgRouter }
|
|||||||
import org.bigbluebutton.core.models.{ UserState, Users2x }
|
import org.bigbluebutton.core.models.{ UserState, Users2x }
|
||||||
import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait }
|
import org.bigbluebutton.core.apps.{ PermissionCheck, RightsManagementTrait }
|
||||||
import org.bigbluebutton.core2.MeetingStatus2x
|
import org.bigbluebutton.core2.MeetingStatus2x
|
||||||
|
import org.bigbluebutton.SystemConfiguration
|
||||||
import scala.util.Random
|
import scala.util.Random
|
||||||
|
|
||||||
trait SelectRandomViewerReqMsgHdlr extends RightsManagementTrait {
|
trait SelectRandomViewerReqMsgHdlr extends RightsManagementTrait {
|
||||||
@ -15,7 +16,7 @@ trait SelectRandomViewerReqMsgHdlr extends RightsManagementTrait {
|
|||||||
def handleSelectRandomViewerReqMsg(msg: SelectRandomViewerReqMsg): Unit = {
|
def handleSelectRandomViewerReqMsg(msg: SelectRandomViewerReqMsg): Unit = {
|
||||||
log.debug("Received SelectRandomViewerReqMsg {}", SelectRandomViewerReqMsg)
|
log.debug("Received SelectRandomViewerReqMsg {}", SelectRandomViewerReqMsg)
|
||||||
|
|
||||||
def broadcastEvent(msg: SelectRandomViewerReqMsg, users: Vector[String], choice: Integer): Unit = {
|
def broadcastEvent(msg: SelectRandomViewerReqMsg, users: Vector[String], choice: String): Unit = {
|
||||||
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, liveMeeting.props.meetingProp.intId, msg.header.userId)
|
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, liveMeeting.props.meetingProp.intId, msg.header.userId)
|
||||||
val envelope = BbbCoreEnvelope(SelectRandomViewerRespMsg.NAME, routing)
|
val envelope = BbbCoreEnvelope(SelectRandomViewerRespMsg.NAME, routing)
|
||||||
val header = BbbClientMsgHeader(SelectRandomViewerRespMsg.NAME, liveMeeting.props.meetingProp.intId, msg.header.userId)
|
val header = BbbClientMsgHeader(SelectRandomViewerRespMsg.NAME, liveMeeting.props.meetingProp.intId, msg.header.userId)
|
||||||
@ -31,11 +32,27 @@ trait SelectRandomViewerReqMsgHdlr extends RightsManagementTrait {
|
|||||||
val reason = "No permission to select random user."
|
val reason = "No permission to select random user."
|
||||||
PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, outGW, liveMeeting)
|
PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, outGW, liveMeeting)
|
||||||
} else {
|
} else {
|
||||||
val users = Users2x.findNotPresentersNorModerators(liveMeeting.users2x)
|
val users = Users2x.getRandomlyPickableUsers(liveMeeting.users2x, false)
|
||||||
|
|
||||||
|
val usersPicked = Users2x.getRandomlyPickableUsers(liveMeeting.users2x, reduceDuplicatedPick)
|
||||||
|
|
||||||
val randNum = new scala.util.Random
|
val randNum = new scala.util.Random
|
||||||
|
val pickedUser = if (usersPicked.size == 0) "" else usersPicked(randNum.nextInt(usersPicked.size)).intId
|
||||||
|
|
||||||
|
if (reduceDuplicatedPick) {
|
||||||
|
if (usersPicked.size == 1) {
|
||||||
|
// Initialise the exemption
|
||||||
|
val usersToUnexempt = Users2x.findAll(liveMeeting.users2x)
|
||||||
|
usersToUnexempt foreach { u =>
|
||||||
|
Users2x.setUserExempted(liveMeeting.users2x, u.intId, false)
|
||||||
|
}
|
||||||
|
} else if (usersPicked.size > 1) {
|
||||||
|
Users2x.setUserExempted(liveMeeting.users2x, pickedUser, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val userIds = users.map { case (v) => v.intId }
|
val userIds = users.map { case (v) => v.intId }
|
||||||
broadcastEvent(msg, userIds, if (users.size == 0) -1 else randNum.nextInt(users.size))
|
broadcastEvent(msg, userIds, pickedUser)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -71,8 +71,13 @@ object Users2x {
|
|||||||
users.toVector.filter(u => !u.presenter)
|
users.toVector.filter(u => !u.presenter)
|
||||||
}
|
}
|
||||||
|
|
||||||
def findNotPresentersNorModerators(users: Users2x): Vector[UserState] = {
|
def getRandomlyPickableUsers(users: Users2x, reduceDup: Boolean): Vector[UserState] = {
|
||||||
users.toVector.filter(u => !u.presenter && u.role != Roles.MODERATOR_ROLE)
|
|
||||||
|
if (reduceDup) {
|
||||||
|
users.toVector.filter(u => !u.presenter && u.role != Roles.MODERATOR_ROLE && !u.userLeftFlag.left && !u.pickExempted)
|
||||||
|
} else {
|
||||||
|
users.toVector.filter(u => !u.presenter && u.role != Roles.MODERATOR_ROLE && !u.userLeftFlag.left)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def findViewers(users: Users2x): Vector[UserState] = {
|
def findViewers(users: Users2x): Vector[UserState] = {
|
||||||
@ -150,6 +155,16 @@ object Users2x {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def setUserExempted(users: Users2x, intId: String, exempted: Boolean): Option[UserState] = {
|
||||||
|
for {
|
||||||
|
u <- findWithIntId(users, intId)
|
||||||
|
} yield {
|
||||||
|
val newUser = u.modify(_.pickExempted).setTo(exempted)
|
||||||
|
users.save(newUser)
|
||||||
|
newUser
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
def hasPresenter(users: Users2x): Boolean = {
|
def hasPresenter(users: Users2x): Boolean = {
|
||||||
findPresenter(users) match {
|
findPresenter(users) match {
|
||||||
case Some(p) => true
|
case Some(p) => true
|
||||||
@ -294,6 +309,7 @@ case class UserState(
|
|||||||
lastActivityTime: Long = System.currentTimeMillis(),
|
lastActivityTime: Long = System.currentTimeMillis(),
|
||||||
lastInactivityInspect: Long = 0,
|
lastInactivityInspect: Long = 0,
|
||||||
clientType: String,
|
clientType: String,
|
||||||
|
pickExempted: Boolean,
|
||||||
userLeftFlag: UserLeftFlag
|
userLeftFlag: UserLeftFlag
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -64,6 +64,7 @@ trait HandlerHelpers extends SystemConfiguration {
|
|||||||
locked = MeetingStatus2x.getPermissions(liveMeeting.status).lockOnJoin,
|
locked = MeetingStatus2x.getPermissions(liveMeeting.status).lockOnJoin,
|
||||||
avatar = regUser.avatarURL,
|
avatar = regUser.avatarURL,
|
||||||
clientType = clientType,
|
clientType = clientType,
|
||||||
|
pickExempted = false,
|
||||||
userLeftFlag = UserLeftFlag(false, 0)
|
userLeftFlag = UserLeftFlag(false, 0)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -69,7 +69,7 @@ trait FakeTestData {
|
|||||||
UserState(intId = regUser.id, extId = regUser.externId, name = regUser.name, role = regUser.role,
|
UserState(intId = regUser.id, extId = regUser.externId, name = regUser.name, role = regUser.role,
|
||||||
guest = regUser.guest, authed = regUser.authed, guestStatus = regUser.guestStatus,
|
guest = regUser.guest, authed = regUser.authed, guestStatus = regUser.guestStatus,
|
||||||
emoji = "none", locked = false, presenter = false, avatar = regUser.avatarURL, clientType = "unknown",
|
emoji = "none", locked = false, presenter = false, avatar = regUser.avatarURL, clientType = "unknown",
|
||||||
userLeftFlag = UserLeftFlag(false, 0))
|
pickExempted = false, userLeftFlag = UserLeftFlag(false, 0))
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -76,6 +76,7 @@ apps {
|
|||||||
ejectOnViolation = false
|
ejectOnViolation = false
|
||||||
endMeetingWhenNoMoreAuthedUsers = false
|
endMeetingWhenNoMoreAuthedUsers = false
|
||||||
endMeetingWhenNoMoreAuthedUsersAfterMinutes = 2
|
endMeetingWhenNoMoreAuthedUsersAfterMinutes = 2
|
||||||
|
reduceDuplicatedPick = false
|
||||||
}
|
}
|
||||||
|
|
||||||
analytics {
|
analytics {
|
||||||
|
@ -409,4 +409,4 @@ case class SelectRandomViewerReqMsgBody(requestedBy: String)
|
|||||||
*/
|
*/
|
||||||
object SelectRandomViewerRespMsg { val NAME = "SelectRandomViewerRespMsg" }
|
object SelectRandomViewerRespMsg { val NAME = "SelectRandomViewerRespMsg" }
|
||||||
case class SelectRandomViewerRespMsg(header: BbbClientMsgHeader, body: SelectRandomViewerRespMsgBody) extends StandardMsg
|
case class SelectRandomViewerRespMsg(header: BbbClientMsgHeader, body: SelectRandomViewerRespMsgBody) extends StandardMsg
|
||||||
case class SelectRandomViewerRespMsgBody(requestedBy: String, userIds: Vector[String], choice: Integer)
|
case class SelectRandomViewerRespMsgBody(requestedBy: String, userIds: Vector[String], choice: String)
|
||||||
|
@ -8,7 +8,7 @@ export default function randomlySelectedUser({ header, body }) {
|
|||||||
check(meetingId, String);
|
check(meetingId, String);
|
||||||
check(requestedBy, String);
|
check(requestedBy, String);
|
||||||
check(userIds, Array);
|
check(userIds, Array);
|
||||||
check(choice, Number);
|
check(choice, String);
|
||||||
|
|
||||||
updateRandomViewer(meetingId, userIds, choice, requestedBy);
|
updateRandomViewer(meetingId, userIds, choice, requestedBy);
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ import { check } from 'meteor/check';
|
|||||||
export default function updateRandomUser(meetingId, userIds, choice, requesterId) {
|
export default function updateRandomUser(meetingId, userIds, choice, requesterId) {
|
||||||
check(meetingId, String);
|
check(meetingId, String);
|
||||||
check(userIds, Array);
|
check(userIds, Array);
|
||||||
check(choice, Number);
|
check(choice, String);
|
||||||
check(requesterId, String);
|
check(requesterId, String);
|
||||||
|
|
||||||
const selector = {
|
const selector = {
|
||||||
@ -13,7 +13,7 @@ export default function updateRandomUser(meetingId, userIds, choice, requesterId
|
|||||||
};
|
};
|
||||||
|
|
||||||
const userList = [];
|
const userList = [];
|
||||||
if (choice < 0) { // no viewer
|
if (choice == "") { // no viewer
|
||||||
userList.push([requesterId,0]);
|
userList.push([requesterId,0]);
|
||||||
} else if (userIds.length == 1) {
|
} else if (userIds.length == 1) {
|
||||||
userList.push([userIds[0],0]);
|
userList.push([userIds[0],0]);
|
||||||
@ -26,7 +26,7 @@ export default function updateRandomUser(meetingId, userIds, choice, requesterId
|
|||||||
}
|
}
|
||||||
userList.push([userId, intervals.shift()]);
|
userList.push([userId, intervals.shift()]);
|
||||||
}
|
}
|
||||||
userList[userList.length-1][0] = userIds[choice]; // last one should be chosen in akka-app
|
userList[userList.length-1][0] = choice; // last one should be chosen in akka-app
|
||||||
}
|
}
|
||||||
|
|
||||||
if (userIds.length == 2) {
|
if (userIds.length == 2) {
|
||||||
|
@ -126,7 +126,8 @@ class RandomUserSelect extends Component {
|
|||||||
const counter = SELECT_RANDOM_USER_COUNTDOWN ? this.state.count : 0;
|
const counter = SELECT_RANDOM_USER_COUNTDOWN ? this.state.count : 0;
|
||||||
if (mappedRandomlySelectedUsers.length < counter + 1) return null;
|
if (mappedRandomlySelectedUsers.length < counter + 1) return null;
|
||||||
|
|
||||||
const selectedUser = mappedRandomlySelectedUsers[counter][0];
|
const selectedUser = SELECT_RANDOM_USER_COUNTDOWN ? mappedRandomlySelectedUsers[counter][0] :
|
||||||
|
mappedRandomlySelectedUsers[mappedRandomlySelectedUsers.length - 1][0];
|
||||||
const countDown = SELECT_RANDOM_USER_COUNTDOWN ?
|
const countDown = SELECT_RANDOM_USER_COUNTDOWN ?
|
||||||
mappedRandomlySelectedUsers.length - this.state.count - 1 : 0;
|
mappedRandomlySelectedUsers.length - this.state.count - 1 : 0;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user