graphql: Add support for Dial-in user

This commit is contained in:
Gustavo Trott 2023-11-16 10:59:08 -03:00
parent 6dc3dbb1fe
commit 779e82fad9
11 changed files with 123 additions and 41 deletions

View File

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

View File

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

View File

@ -1,6 +1,6 @@
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.LockSettingsUtil
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.models._
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.TimeUtil
import scala.collection.immutable.Map
import scala.concurrent.duration._
@ -323,6 +325,8 @@ object VoiceApp extends SystemConfiguration {
uuid
)
VoiceUsers.add(liveMeeting.voiceUsers, voiceUserState)
UserVoiceDAO.update(voiceUserState)
UserDAO.updateVoiceUserJoined(voiceUserState)
broadcastEvent(voiceUserState)

View File

@ -1,7 +1,7 @@
package org.bigbluebutton.core.apps.voice
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 }
trait VoiceConfCallStateEvtMsgHdlr {
@ -38,5 +38,9 @@ trait VoiceConfCallStateEvtMsgHdlr {
val event = VoiceCallStateEvtMsg(header, body)
val msgEvent = BbbCommonEnvCoreMsg(envelope, event)
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
import org.bigbluebutton.core.models.{RegisteredUser}
import org.bigbluebutton.core.models.{RegisteredUser, VoiceUserState}
import slick.jdbc.PostgresProfile.api._
import scala.concurrent.ExecutionContext.Implicits.global
@ -100,6 +100,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) = {
DatabaseConnection.db.run(
TableQuery[UserDbTableDef]
@ -114,15 +127,6 @@ object UserDAO {
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(
TableQuery[UserDbTableDef]
.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 }
case class UserVoiceDbModel(
userId: String,
voiceUserId: String,
callerName: String,
callerNum: String,
callingWith: String,
joined: Boolean,
listenOnly: Boolean,
muted: Boolean,
spoke: Boolean,
talking: Boolean,
floor: Boolean,
lastFloorTime: String,
voiceConf: String,
startTime: Option[Long],
endTime: Option[Long],
userId: String,
voiceUserId: String,
callerName: String,
callerNum: String,
callingWith: String,
joined: Boolean,
listenOnly: Boolean,
muted: Boolean,
spoke: Boolean,
talking: Boolean,
floor: Boolean,
lastFloorTime: String,
startTime: Option[Long],
endTime: Option[Long],
)
class UserVoiceDbTableDef(tag: Tag) extends Table[UserVoiceDbModel](tag, None, "user_voice") {
override def * = (
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)
val userId = column[String]("userId", O.PrimaryKey)
val voiceUserId = column[String]("voiceUserId")
@ -42,6 +41,9 @@ class UserVoiceDbTableDef(tag: Tag) extends Table[UserVoiceDbModel](tag, None, "
val floor = column[Boolean]("floor")
val lastFloorTime = column[String]("lastFloorTime")
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 endTime = column[Option[Long]]("endTime")
}
@ -64,7 +66,6 @@ object UserVoiceDAO {
talking = voiceUserState.talking,
floor = voiceUserState.floor,
lastFloorTime = voiceUserState.lastFloorTime,
voiceConf = "",
startTime = None,
endTime = None
)
@ -85,7 +86,7 @@ object UserVoiceDAO {
.update((voiceUserState.listenOnly, voiceUserState.muted, voiceUserState.floor, voiceUserState.lastFloorTime))
).onComplete {
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
// muted: false
// talking: false
@ -124,10 +137,11 @@ object UserVoiceDAO {
DatabaseConnection.db.run(
TableQuery[UserVoiceDbTableDef]
.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 {
case Success(rowsAffected) => DatabaseConnection.logger.debug(s"Voice of user ${userId} deleted")
case Failure(e) => DatabaseConnection.logger.error(s"Error deleting voice: $e")
case Success(rowsAffected) => DatabaseConnection.logger.debug(s"Voice of user ${userId} deleted (joined=false)")
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] = {
UserVoiceDAO.deleteUser(intId)
UserVoiceDAO.deleteUserVoice(intId)
users.remove(intId)
}

View File

@ -249,8 +249,8 @@ CREATE TABLE "user" (
"guestLobbyMessage" text,
"mobile" bool,
"clientType" varchar(50),
"disconnected" bool, -- 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
"disconnected" bool default false, -- this is the old leftFlag (that was renamed), set when the user just closed the client
"expired" bool default false, -- when it is been some time the user is disconnected
"ejected" bool,
"ejectReason" varchar(255),
"ejectReasonCode" varchar(50),
@ -485,7 +485,7 @@ join meeting_welcome w USING("meetingId");
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),
"callerName" varchar(100),
"callerNum" varchar(100),
@ -498,6 +498,9 @@ CREATE TABLE "user_voice" (
"floor" boolean,
"lastFloorTime" varchar(25),
"voiceConf" varchar(100),
"voiceConfCallSession" varchar(50),
"voiceConfClientSession" varchar(10),
"voiceConfCallState" varchar(30),
"endTime" bigint,
"startTime" bigint
);
@ -517,7 +520,8 @@ SELECT
FROM "user" u
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)
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" (
"streamId" varchar(100) PRIMARY KEY,

View File

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

View File

@ -1,8 +1,8 @@
- "!include public_v_caption.yaml"
- "!include public_v_breakoutRoom.yaml"
- "!include public_v_breakoutRoom_assignedUser.yaml"
- "!include public_v_breakoutRoom_participant.yaml"
- "!include public_v_breakoutRoom_user.yaml"
- "!include public_v_caption.yaml"
- "!include public_v_chat.yaml"
- "!include public_v_chat_message_private.yaml"
- "!include public_v_chat_message_public.yaml"