Merge pull request #19159 from gustavotrott/graphql-support-dial-in-user

graphql: Add support for Dial-in user
This commit is contained in:
Gustavo Trott 2023-11-21 12:16:09 -03:00 committed by GitHub
commit 5a69742983
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 122 additions and 40 deletions

View File

@ -57,7 +57,7 @@ trait UserJoinedVoiceConfEvtMsgHdlr extends SystemConfiguration {
locked = MeetingStatus2x.getPermissions(liveMeeting.status).lockOnJoin, locked = MeetingStatus2x.getPermissions(liveMeeting.status).lockOnJoin,
avatar = "", avatar = "",
color = userColor, color = userColor,
clientType = "", clientType = if (isDialInUser) "dial-in-user" else "",
pickExempted = false, pickExempted = false,
userLeftFlag = UserLeftFlag(false, 0) userLeftFlag = UserLeftFlag(false, 0)
) )

View File

@ -4,6 +4,7 @@ import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.models._ import org.bigbluebutton.core.models._
import org.bigbluebutton.core.apps.users.UsersApp import org.bigbluebutton.core.apps.users.UsersApp
import org.bigbluebutton.core.apps.breakout.BreakoutHdlrHelpers import org.bigbluebutton.core.apps.breakout.BreakoutHdlrHelpers
import org.bigbluebutton.core.db.UserDAO
import org.bigbluebutton.core.running.{ LiveMeeting, MeetingActor, OutMsgRouter } import org.bigbluebutton.core.running.{ LiveMeeting, MeetingActor, OutMsgRouter }
trait UserLeftVoiceConfEvtMsgHdlr { trait UserLeftVoiceConfEvtMsgHdlr {
@ -39,6 +40,7 @@ trait UserLeftVoiceConfEvtMsgHdlr {
UsersApp.guestWaitingLeft(liveMeeting, user.intId, outGW) UsersApp.guestWaitingLeft(liveMeeting, user.intId, outGW)
} }
Users2x.remove(liveMeeting.users2x, user.intId) Users2x.remove(liveMeeting.users2x, user.intId)
UserDAO.delete(user.intId)
VoiceApp.removeUserFromVoiceConf(liveMeeting, outGW, msg.body.voiceUserId) VoiceApp.removeUserFromVoiceConf(liveMeeting, outGW, msg.body.voiceUserId)
} }

View File

@ -1,6 +1,6 @@
package org.bigbluebutton.core.apps.voice package org.bigbluebutton.core.apps.voice
import org.apache.pekko.actor.{ ActorContext, ActorSystem, Cancellable } import org.apache.pekko.actor.{ActorContext, ActorSystem, Cancellable}
import org.bigbluebutton.SystemConfiguration import org.bigbluebutton.SystemConfiguration
import org.bigbluebutton.LockSettingsUtil import org.bigbluebutton.LockSettingsUtil
import org.bigbluebutton.core.apps.breakout.BreakoutHdlrHelpers import org.bigbluebutton.core.apps.breakout.BreakoutHdlrHelpers
@ -11,8 +11,10 @@ import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.running.{LiveMeeting, MeetingActor, OutMsgRouter} import org.bigbluebutton.core.running.{LiveMeeting, MeetingActor, OutMsgRouter}
import org.bigbluebutton.core.models._ import org.bigbluebutton.core.models._
import org.bigbluebutton.core.apps.users.UsersApp import org.bigbluebutton.core.apps.users.UsersApp
import org.bigbluebutton.core.db.{UserDAO, UserVoiceDAO}
import org.bigbluebutton.core.util.ColorPicker import org.bigbluebutton.core.util.ColorPicker
import org.bigbluebutton.core.util.TimeUtil import org.bigbluebutton.core.util.TimeUtil
import scala.collection.immutable.Map import scala.collection.immutable.Map
import scala.concurrent.duration._ import scala.concurrent.duration._
@ -323,6 +325,8 @@ object VoiceApp extends SystemConfiguration {
uuid uuid
) )
VoiceUsers.add(liveMeeting.voiceUsers, voiceUserState) VoiceUsers.add(liveMeeting.voiceUsers, voiceUserState)
UserVoiceDAO.update(voiceUserState)
UserDAO.updateVoiceUserJoined(voiceUserState)
broadcastEvent(voiceUserState) broadcastEvent(voiceUserState)

View File

@ -1,7 +1,7 @@
package org.bigbluebutton.core.apps.voice package org.bigbluebutton.core.apps.voice
import org.bigbluebutton.common2.msgs.{ BbbClientMsgHeader, BbbCommonEnvCoreMsg, BbbCoreEnvelope, MessageTypes, Routing, VoiceCallStateEvtMsg, VoiceCallStateEvtMsgBody, VoiceConfCallStateEvtMsg } import org.bigbluebutton.common2.msgs.{ BbbClientMsgHeader, BbbCommonEnvCoreMsg, BbbCoreEnvelope, MessageTypes, Routing, VoiceCallStateEvtMsg, VoiceCallStateEvtMsgBody, VoiceConfCallStateEvtMsg }
import org.bigbluebutton.core.models.{ VoiceUserState, VoiceUsers } import org.bigbluebutton.core.db.{ UserVoiceConfStateDAO, UserVoiceDAO }
import org.bigbluebutton.core.running.{ LiveMeeting, MeetingActor, OutMsgRouter } import org.bigbluebutton.core.running.{ LiveMeeting, MeetingActor, OutMsgRouter }
trait VoiceConfCallStateEvtMsgHdlr { trait VoiceConfCallStateEvtMsgHdlr {
@ -38,5 +38,9 @@ trait VoiceConfCallStateEvtMsgHdlr {
val event = VoiceCallStateEvtMsg(header, body) val event = VoiceCallStateEvtMsg(header, body)
val msgEvent = BbbCommonEnvCoreMsg(envelope, event) val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
outGW.send(msgEvent) outGW.send(msgEvent)
if (msg.body.userId.nonEmpty) {
UserVoiceConfStateDAO.insertOrUpdate(msg.body.userId, msg.body.voiceConf, msg.body.callSession, msg.body.clientSession, msg.body.callState)
}
} }
} }

View File

@ -1,5 +1,5 @@
package org.bigbluebutton.core.db package org.bigbluebutton.core.db
import org.bigbluebutton.core.models.{RegisteredUser} import org.bigbluebutton.core.models.{RegisteredUser, VoiceUserState}
import slick.jdbc.PostgresProfile.api._ import slick.jdbc.PostgresProfile.api._
import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.ExecutionContext.Implicits.global
@ -106,6 +106,19 @@ object UserDAO {
} }
} }
def updateVoiceUserJoined(voiceUserState: VoiceUserState) = {
DatabaseConnection.db.run(
TableQuery[UserDbTableDef]
.filter(_.userId === voiceUserState.intId)
.map(u => (u.guest, u.guestStatus, u.authed, u.joined))
.update((false, "ALLOW", true, true))
).onComplete {
case Success(rowsAffected) => DatabaseConnection.logger.debug(s"$rowsAffected row(s) updated on user voice table!")
case Failure(e) => DatabaseConnection.logger.debug(s"Error updating user voice: $e")
}
}
def updateJoinError(userId: String, joinErrorCode: String, joinErrorMessage: String) = { def updateJoinError(userId: String, joinErrorCode: String, joinErrorMessage: String) = {
DatabaseConnection.db.run( DatabaseConnection.db.run(
TableQuery[UserDbTableDef] TableQuery[UserDbTableDef]
@ -120,15 +133,6 @@ object UserDAO {
def delete(intId: String) = { def delete(intId: String) = {
// DatabaseConnection.db.run(
// TableQuery[UserDbTableDef]
// .filter(_.userId === intId)
// .delete
// ).onComplete {
// case Success(rowsAffected) => DatabaseConnection.logger.debug(s"User ${intId} deleted")
// case Failure(e) => DatabaseConnection.logger.error(s"Error deleting user ${intId}: $e")
// }
DatabaseConnection.db.run( DatabaseConnection.db.run(
TableQuery[UserDbTableDef] TableQuery[UserDbTableDef]
.filter(_.userId === intId) .filter(_.userId === intId)

View File

@ -0,0 +1,49 @@
package org.bigbluebutton.core.db
import org.bigbluebutton.core.models.{ VoiceUserState }
import slick.jdbc.PostgresProfile.api._
import scala.concurrent.ExecutionContext.Implicits.global
import scala.util.{Failure, Success }
case class UserVoiceConfStateDbModel(
userId: String,
voiceConf: String,
voiceConfCallSession: String,
voiceConfClientSession: String,
voiceConfCallState: String,
)
class UserVoiceConfStateDbTableDef(tag: Tag) extends Table[UserVoiceConfStateDbModel](tag, None, "user_voice") {
override def * = (
userId, voiceConf, voiceConfCallSession, voiceConfClientSession, voiceConfCallState
) <> (UserVoiceConfStateDbModel.tupled, UserVoiceConfStateDbModel.unapply)
val userId = column[String]("userId", O.PrimaryKey)
val voiceConf = column[String]("voiceConf")
val voiceConfCallSession = column[String]("voiceConfCallSession")
val voiceConfClientSession = column[String]("voiceConfClientSession")
val voiceConfCallState = column[String]("voiceConfCallState")
}
object UserVoiceConfStateDAO {
def insertOrUpdate(userId: String, voiceConf: String, voiceConfCallSession: String, clientSession: String, callState: String) = {
DatabaseConnection.db.run(
TableQuery[UserVoiceConfStateDbTableDef].insertOrUpdate(
UserVoiceConfStateDbModel(
userId = userId,
voiceConf = voiceConf,
voiceConfCallSession = voiceConfCallSession,
voiceConfClientSession = clientSession,
voiceConfCallState = callState,
)
)
).onComplete {
case Success(rowsAffected) => {
DatabaseConnection.logger.debug(s"$rowsAffected row(s) inserted on user_voice table!")
}
case Failure(e) => DatabaseConnection.logger.debug(s"Error inserting voice: $e")
}
}
}

View File

@ -7,27 +7,26 @@ import scala.concurrent.ExecutionContext.Implicits.global
import scala.util.{Failure, Success } import scala.util.{Failure, Success }
case class UserVoiceDbModel( case class UserVoiceDbModel(
userId: String, userId: String,
voiceUserId: String, voiceUserId: String,
callerName: String, callerName: String,
callerNum: String, callerNum: String,
callingWith: String, callingWith: String,
joined: Boolean, joined: Boolean,
listenOnly: Boolean, listenOnly: Boolean,
muted: Boolean, muted: Boolean,
spoke: Boolean, spoke: Boolean,
talking: Boolean, talking: Boolean,
floor: Boolean, floor: Boolean,
lastFloorTime: String, lastFloorTime: String,
voiceConf: String, startTime: Option[Long],
startTime: Option[Long], endTime: Option[Long],
endTime: Option[Long],
) )
class UserVoiceDbTableDef(tag: Tag) extends Table[UserVoiceDbModel](tag, None, "user_voice") { class UserVoiceDbTableDef(tag: Tag) extends Table[UserVoiceDbModel](tag, None, "user_voice") {
override def * = ( override def * = (
userId, voiceUserId, callerName, callerNum, callingWith, joined, listenOnly, userId, voiceUserId, callerName, callerNum, callingWith, joined, listenOnly,
muted, spoke, talking, floor, lastFloorTime, voiceConf, startTime, endTime muted, spoke, talking, floor, lastFloorTime, startTime, endTime
) <> (UserVoiceDbModel.tupled, UserVoiceDbModel.unapply) ) <> (UserVoiceDbModel.tupled, UserVoiceDbModel.unapply)
val userId = column[String]("userId", O.PrimaryKey) val userId = column[String]("userId", O.PrimaryKey)
val voiceUserId = column[String]("voiceUserId") val voiceUserId = column[String]("voiceUserId")
@ -42,6 +41,9 @@ class UserVoiceDbTableDef(tag: Tag) extends Table[UserVoiceDbModel](tag, None, "
val floor = column[Boolean]("floor") val floor = column[Boolean]("floor")
val lastFloorTime = column[String]("lastFloorTime") val lastFloorTime = column[String]("lastFloorTime")
val voiceConf = column[String]("voiceConf") val voiceConf = column[String]("voiceConf")
val voiceConfCallSession = column[String]("voiceConfCallSession")
val voiceConfClientSession = column[String]("voiceConfClientSession")
val voiceConfCallState = column[String]("voiceConfCallState")
val startTime = column[Option[Long]]("startTime") val startTime = column[Option[Long]]("startTime")
val endTime = column[Option[Long]]("endTime") val endTime = column[Option[Long]]("endTime")
} }
@ -64,7 +66,6 @@ object UserVoiceDAO {
talking = voiceUserState.talking, talking = voiceUserState.talking,
floor = voiceUserState.floor, floor = voiceUserState.floor,
lastFloorTime = voiceUserState.lastFloorTime, lastFloorTime = voiceUserState.lastFloorTime,
voiceConf = "",
startTime = None, startTime = None,
endTime = None endTime = None
) )
@ -85,7 +86,7 @@ object UserVoiceDAO {
.update((voiceUserState.listenOnly, voiceUserState.muted, voiceUserState.floor, voiceUserState.lastFloorTime)) .update((voiceUserState.listenOnly, voiceUserState.muted, voiceUserState.floor, voiceUserState.lastFloorTime))
).onComplete { ).onComplete {
case Success(rowsAffected) => DatabaseConnection.logger.debug(s"$rowsAffected row(s) updated on user_voice table!") case Success(rowsAffected) => DatabaseConnection.logger.debug(s"$rowsAffected row(s) updated on user_voice table!")
case Failure(e) => DatabaseConnection.logger.error(s"Error updating user: $e") case Failure(e) => DatabaseConnection.logger.error(s"Error updating user_voice: $e")
} }
} }
@ -113,7 +114,19 @@ object UserVoiceDAO {
} }
} }
def deleteUser(userId: String) = { def delete(intId: String) = {
DatabaseConnection.db.run(
TableQuery[UserDbTableDef]
.filter(_.userId === intId)
.map(u => (u.loggedOut))
.update((true))
).onComplete {
case Success(rowsAffected) => DatabaseConnection.logger.debug(s"$rowsAffected row(s) updated loggedOut=true on user table!")
case Failure(e) => DatabaseConnection.logger.error(s"Error updating loggedOut=true user: $e")
}
}
def deleteUserVoice(userId: String) = {
//Meteor sets this props instead of removing //Meteor sets this props instead of removing
// muted: false // muted: false
// talking: false // talking: false
@ -124,10 +137,11 @@ object UserVoiceDAO {
DatabaseConnection.db.run( DatabaseConnection.db.run(
TableQuery[UserVoiceDbTableDef] TableQuery[UserVoiceDbTableDef]
.filter(_.userId === userId) .filter(_.userId === userId)
.delete .map(u => (u.muted, u.talking, u.listenOnly, u.joined, u.spoke, u.startTime, u.endTime))
.update((false, false, false, false, false, None, None))
).onComplete { ).onComplete {
case Success(rowsAffected) => DatabaseConnection.logger.debug(s"Voice of user ${userId} deleted") case Success(rowsAffected) => DatabaseConnection.logger.debug(s"Voice of user ${userId} deleted (joined=false)")
case Failure(e) => DatabaseConnection.logger.error(s"Error deleting voice: $e") case Failure(e) => DatabaseConnection.logger.error(s"Error deleting voice user: $e")
} }
} }

View File

@ -39,7 +39,7 @@ object VoiceUsers {
} }
def removeWithIntId(users: VoiceUsers, intId: String): Option[VoiceUserState] = { def removeWithIntId(users: VoiceUsers, intId: String): Option[VoiceUserState] = {
UserVoiceDAO.deleteUser(intId) UserVoiceDAO.deleteUserVoice(intId)
users.remove(intId) users.remove(intId)
} }

View File

@ -250,8 +250,8 @@ CREATE TABLE "user" (
"guestLobbyMessage" text, "guestLobbyMessage" text,
"mobile" bool, "mobile" bool,
"clientType" varchar(50), "clientType" varchar(50),
"disconnected" bool, -- this is the old leftFlag (that was renamed), set when the user just closed the client "disconnected" bool default false, -- this is the old leftFlag (that was renamed), set when the user just closed the client
"expired" bool, -- when it is been some time the user is disconnected "expired" bool default false, -- when it is been some time the user is disconnected
"ejected" bool, "ejected" bool,
"ejectReason" varchar(255), "ejectReason" varchar(255),
"ejectReasonCode" varchar(50), "ejectReasonCode" varchar(50),
@ -487,7 +487,7 @@ join meeting_welcome w USING("meetingId");
CREATE TABLE "user_voice" ( CREATE TABLE "user_voice" (
"userId" varchar(50) PRIMARY KEY NOT NULL REFERENCES "user"("userId") ON DELETE CASCADE, "userId" varchar(50) PRIMARY KEY NOT NULL REFERENCES "user"("userId") ON DELETE CASCADE,
"voiceUserId" varchar(100), "voiceUserId" varchar(100),
"callerName" varchar(100), "callerName" varchar(100),
"callerNum" varchar(100), "callerNum" varchar(100),
@ -500,6 +500,9 @@ CREATE TABLE "user_voice" (
"floor" boolean, "floor" boolean,
"lastFloorTime" varchar(25), "lastFloorTime" varchar(25),
"voiceConf" varchar(100), "voiceConf" varchar(100),
"voiceConfCallSession" varchar(50),
"voiceConfClientSession" varchar(10),
"voiceConfCallState" varchar(30),
"endTime" bigint, "endTime" bigint,
"startTime" bigint "startTime" bigint
); );
@ -519,7 +522,8 @@ SELECT
FROM "user" u FROM "user" u
JOIN "user_voice" ON "user_voice"."userId" = u."userId" JOIN "user_voice" ON "user_voice"."userId" = u."userId"
LEFT JOIN "user_voice" user_talking ON (user_talking."userId" = u."userId" and user_talking."talking" IS TRUE) LEFT JOIN "user_voice" user_talking ON (user_talking."userId" = u."userId" and user_talking."talking" IS TRUE)
OR (user_talking."userId" = u."userId" and user_talking."hideTalkingIndicatorAt" > now()); OR (user_talking."userId" = u."userId" and user_talking."hideTalkingIndicatorAt" > now())
WHERE "user_voice"."joined" is true;
CREATE TABLE "user_camera" ( CREATE TABLE "user_camera" (
"streamId" varchar(100) PRIMARY KEY, "streamId" varchar(100) PRIMARY KEY,

View File

@ -36,6 +36,7 @@ select_permissions:
- talking - talking
- userId - userId
- voiceConf - voiceConf
- voiceConfCallState
- voiceUserId - voiceUserId
filter: filter:
meetingId: meetingId: