Merge remote-tracking branch 'upstream/master' into join-components-with-skeleton
Conflicts: bigbluebutton-html5/imports/api/verto/index.js bigbluebutton-html5/imports/ui/components/deskshare/service.js
This commit is contained in:
commit
a95ed82293
2
.gitignore
vendored
2
.gitignore
vendored
@ -6,6 +6,8 @@ record-and-playback/playback-web/playback-web-0.1.war
|
||||
record-and-playback/.project
|
||||
push_to_git.py
|
||||
**/target/*
|
||||
**/.cache-main
|
||||
**/.cache-tests
|
||||
.vagrant/*
|
||||
**/.settings/*
|
||||
*/.gradle
|
||||
|
579
DEVELOPMENT.md
Normal file
579
DEVELOPMENT.md
Normal file
@ -0,0 +1,579 @@
|
||||
This document provides instructions for developers to setup their
|
||||
environment and work on the upcoming BBB 1.1 (tentative release version).
|
||||
|
||||
## Install BBB 1.0
|
||||
|
||||
Make sure you have a working BBB 1.0 before you proceed with the instructions below.
|
||||
|
||||
## Install OpenJDK 8
|
||||
|
||||
```
|
||||
sudo add-apt-repository ppa:openjdk-r/ppa
|
||||
sudo apt-get update
|
||||
sudo apt-get install openjdk-8-jdk
|
||||
```
|
||||
|
||||
Change the default jre. Choose Java 8.
|
||||
|
||||
```
|
||||
sudo update-alternatives --config java
|
||||
```
|
||||
|
||||
Change the default jdk. Choose Jdk8
|
||||
|
||||
```
|
||||
sudo update-alternatives --config javac
|
||||
```
|
||||
|
||||
## Environment Variables
|
||||
|
||||
Edit `~/.profile` and change `JAVA_HOME`
|
||||
|
||||
```
|
||||
export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64
|
||||
```
|
||||
|
||||
Save the file and refresh environment vars.
|
||||
|
||||
```
|
||||
source ~/.profile
|
||||
```
|
||||
|
||||
## Update Development Tools
|
||||
|
||||
### Install The Core Development Tools
|
||||
|
||||
```
|
||||
sudo apt-get install git-core ant
|
||||
```
|
||||
|
||||
|
||||
### Install Gradle
|
||||
|
||||
```
|
||||
cd ~/dev/tools
|
||||
wget http://services.gradle.org/distributions/gradle-2.12-bin.zip
|
||||
unzip gradle-2.12-bin.zip
|
||||
ln -s gradle-2.12 gradle
|
||||
```
|
||||
|
||||
### Install Grails
|
||||
|
||||
```
|
||||
cd ~/dev/tools
|
||||
wget https://github.com/grails/grails-core/releases/download/v2.5.2/grails-2.5.2.zip
|
||||
unzip grails-2.5.2.zip
|
||||
ln -s grails-2.5.2 grails
|
||||
```
|
||||
|
||||
### Install Maven
|
||||
|
||||
```
|
||||
cd ~/dev/tools
|
||||
wget apache.parentingamerica.com//maven/maven-3/3.3.3/binaries/apache-maven-3.3.3-bin.zip
|
||||
unzip apache-maven-3.3.3-bin.zip
|
||||
ln -s apache-maven-3.3.3 maven
|
||||
```
|
||||
|
||||
### Install sbt
|
||||
|
||||
```
|
||||
cd ~/dev/tools
|
||||
wget https://dl.bintray.com/sbt/native-packages/sbt/0.13.9/sbt-0.13.9.tgz
|
||||
tar zxvf sbt-0.13.9.tgz
|
||||
```
|
||||
|
||||
In the next step, you need to get the Apache Flex 4.13.0 SDK package.
|
||||
|
||||
**Note:** Even though we're downloading the Apache Flex 4.13.0 SDK, BigBlueButton is developed and built with Flex 3 compatibility mode enabled.
|
||||
|
||||
First, you need to download the SDK tarball from an Apache mirror site and then unpack it.
|
||||
|
||||
```
|
||||
wget https://archive.apache.org/dist/flex/4.13.0/binaries/apache-flex-sdk-4.13.0-bin.tar.gz
|
||||
tar xvfz apache-flex-sdk-4.13.0-bin.tar.gz
|
||||
```
|
||||
|
||||
Once Flex SDK is unpacked, you need to download the Adobe Flex SDK. We'll do this step manually in case the download fails (if it does, remove the incomplete file and issue the `wget` command again).
|
||||
|
||||
```
|
||||
cd apache-flex-sdk-4.13.0-bin/
|
||||
mkdir -p in/
|
||||
wget http://download.macromedia.com/pub/flex/sdk/builds/flex4.6/flex_sdk_4.6.0.23201B.zip -P in/
|
||||
```
|
||||
|
||||
Once the SDK has downloaded, we can use its `build.xml` script to automatically download the remaining third-party tools.
|
||||
|
||||
```
|
||||
ant -f frameworks/build.xml thirdparty-downloads
|
||||
```
|
||||
|
||||
After Flex downloads the remaining third-party tools, you need to modify their permissions.
|
||||
|
||||
```
|
||||
sudo find ~/dev/tools/apache-flex-sdk-4.13.0-bin -type d -exec chmod o+rx '{}' \;
|
||||
chmod 755 ~/dev/tools/apache-flex-sdk-4.13.0-bin/bin/*
|
||||
sudo chmod -R +r ~/dev/tools/apache-flex-sdk-4.13.0-bin
|
||||
```
|
||||
|
||||
Next, create a linked directory with a shortened name for easier referencing.
|
||||
|
||||
```
|
||||
ln -s ~/dev/tools/apache-flex-sdk-4.13.0-bin ~/dev/tools/flex
|
||||
```
|
||||
|
||||
The next step in setting up the Flex SDK environment is to download a Flex library for video.
|
||||
|
||||
```
|
||||
cd ~/dev/tools/
|
||||
mkdir -p apache-flex-sdk-4.13.0-bin/frameworks/libs/player/11.2
|
||||
cd apache-flex-sdk-4.13.0-bin/frameworks/libs/player/11.2
|
||||
wget http://fpdownload.macromedia.com/get/flashplayer/installers/archive/playerglobal/playerglobal11_2.swc
|
||||
mv -f playerglobal11_2.swc playerglobal.swc
|
||||
```
|
||||
|
||||
The last step to have a working Flex SDK is to configure it to work with playerglobal 11.2
|
||||
|
||||
```
|
||||
cd ~/dev/tools/apache-flex-sdk-4.13.0-bin
|
||||
sudo sed -i "s/11.1/11.2/g" frameworks/flex-config.xml
|
||||
sudo sed -i "s/<swf-version>14<\/swf-version>/<swf-version>15<\/swf-version>/g" frameworks/flex-config.xml
|
||||
sudo sed -i "s/{playerglobalHome}\/{targetPlayerMajorVersion}.{targetPlayerMinorVersion}/libs\/player\/11.2/g" frameworks/flex-config.xml
|
||||
```
|
||||
|
||||
With the tools installed, you need to add a set of environment variables to your `.profile` to access these tools.
|
||||
|
||||
```
|
||||
vi ~/.profile
|
||||
```
|
||||
|
||||
Copy-and-paste the following text at bottom of `.profile`.
|
||||
|
||||
```
|
||||
|
||||
export GRAILS_HOME=$HOME/dev/tools/grails
|
||||
export PATH=$PATH:$GRAILS_HOME/bin
|
||||
|
||||
export FLEX_HOME=$HOME/dev/tools/flex
|
||||
export PATH=$PATH:$FLEX_HOME/bin
|
||||
|
||||
export GRADLE_HOME=$HOME/dev/tools/gradle
|
||||
export PATH=$PATH:$GRADLE_HOME/bin
|
||||
|
||||
export SBT_HOME=$HOME/dev/tools/sbt
|
||||
export PATH=$PATH:$SBT_HOME/bin
|
||||
|
||||
export MAVEN_HOME=$HOME/dev/tools/maven
|
||||
export PATH=$PATH:$MAVEN_HOME/bin
|
||||
|
||||
|
||||
```
|
||||
|
||||
Reload your profile to use these tools (this will happen automatically when you next login).
|
||||
|
||||
```
|
||||
source ~/.profile
|
||||
```
|
||||
|
||||
Check that the tools are now in your path by running the following command.
|
||||
|
||||
```
|
||||
$ mxmlc -version
|
||||
Version 4.13.0 build 20140701
|
||||
```
|
||||
|
||||
## Setup Red5
|
||||
|
||||
```
|
||||
cd /usr/share
|
||||
|
||||
# Make a backup of the deployed red5
|
||||
|
||||
sudo mv red5 red5-orig
|
||||
|
||||
# Symlink red5 to old red5
|
||||
|
||||
sudo ln -s red5-orig red5
|
||||
```
|
||||
|
||||
## Build Red5
|
||||
|
||||
Build red5-parent
|
||||
|
||||
```
|
||||
cd ~/dev
|
||||
git clone https://github.com/bigbluebutton/red5-parent.git
|
||||
cd red5-parent/
|
||||
git checkout snapshot-mar-30-2016
|
||||
mvn install
|
||||
```
|
||||
|
||||
Build red5-io
|
||||
|
||||
```
|
||||
cd ~/dev
|
||||
git clone https://github.com/bigbluebutton/red5-io.git
|
||||
cd red5-io
|
||||
git checkout snapshot-mar-30-2016
|
||||
./bbb-build.sh
|
||||
```
|
||||
|
||||
Build red5-server-common
|
||||
|
||||
```
|
||||
cd ~/dev
|
||||
git clone https://github.com/bigbluebutton/red5-server-common.git
|
||||
cd red5-server-common
|
||||
git checkout snapshot-mar-30-2016
|
||||
./bbb-build.sh
|
||||
```
|
||||
|
||||
Build red5-server
|
||||
|
||||
```
|
||||
cd ~/dev
|
||||
git clone https://github.com/bigbluebutton/red5-server.git
|
||||
cd red5-server
|
||||
git checkout snapshot-mar-30-2016
|
||||
./build-red5.sh
|
||||
|
||||
# Deploy red5, this will copy the new red5 to /usr/share
|
||||
# and modify the symlink you created above.
|
||||
|
||||
./deploy-red5.sh
|
||||
```
|
||||
|
||||
# Developing the client
|
||||
|
||||
|
||||
# Client Development
|
||||
With the development environment checked out and the code cloned, we are ready to start developing!
|
||||
|
||||
This section will walk you through making a simple change to the BigBlueButton client.
|
||||
|
||||
## Setting up the environment
|
||||
|
||||
The first thing you need to do is to copy the template `config.xml` file to the build directory for the client.
|
||||
|
||||
```
|
||||
cd ~/dev/bigbluebutton/
|
||||
cp bigbluebutton-client/resources/config.xml.template bigbluebutton-client/src/conf/config.xml
|
||||
```
|
||||
|
||||
The `config.xml` file is one of the first files loaded by the BigBlueButton client when it connects to the server. The `config.xml` file tells BigBlueButton client how to load the remaining components (such as chat module, deskshare module, video conf module, etc.) and sets a number of configuration parameters for each component. The `config.xml` specifies the hostname (or IP address) for loading each component.
|
||||
|
||||
Let's look at the first ten lines of the `config.xml` file you just copied.
|
||||
|
||||
```
|
||||
$ head -n 10 bigbluebutton-client/src/conf/config.xml
|
||||
<?xml version="1.0" ?>
|
||||
<config>
|
||||
<localeversion suppressWarning="false">0.9.0</localeversion>
|
||||
<version>VERSION</version>
|
||||
<help url="http://HOST/help.html"/>
|
||||
<javaTest url="http://HOST/testjava.html"/>
|
||||
<porttest host="HOST" application="video/portTest" timeout="10000"/>
|
||||
<bwMon server="HOST" application="video/bwTest"/>
|
||||
<application uri="rtmp://HOST/bigbluebutton" host="http://HOST/bigbluebutton/api/enter"/>
|
||||
<language userSelectionEnabled="true" />
|
||||
```
|
||||
|
||||
You will see the word `HOST` where there would be configured hostname/IP address. You need to change the text `HOST` to the IP address (or hostname) of your BigBlueButton server. For example, if the IP address of your BigBlueButton server is `192.168.1.145`, then using the following command you can easily substitute all occurrences of `HOST` with `192.168.1.145`.
|
||||
|
||||
Note: Don't copy-and-paste the following command as-is: the address `192.168.1.145` is likely not the correct IP address (or hostname) for your BigBlueButton server. Substitute the IP address (or hostname) for your BigBlueButton server.
|
||||
|
||||
```
|
||||
sed -i s/HOST/192.168.1.145/g bigbluebutton-client/src/conf/config.xml
|
||||
```
|
||||
|
||||
After you've done the above command, take a quick look at the file and ensure all instances of `HOST` are properly replaced with the IP address (or hostname) of your BigBlueButton server.
|
||||
|
||||
The `config.xml` is ultimately loaded by the BigBlueButton client when a user joins a session on the server.
|
||||
|
||||
Later on, when you deploy your modified client to the BigBlueButton server, there will be two BigBlueButton clients on your server: your modified BigBlueButton client and the default BigBlueButton packaged client (again, this is good as you can switch back and forth). However, the BigBlueButton configuration command `sudo bbb-conf ` only modifies the packaged BigBlueButton client and you will need to mirror any changes to the packaged config.xml to the secondary client's config.xml.
|
||||
|
||||
Next, you need to setup nginx to redirect calls to the client towards your development version. If you don't already have nginx client development file at `/etc/bigbluebutton/nginx/client_dev`, create one with the following command.
|
||||
|
||||
**NOTE:** Make sure to replace "firstuser" with your own username if it's different.
|
||||
|
||||
```
|
||||
echo "
|
||||
location /client/BigBlueButton.html {
|
||||
root /home/firstuser/dev/bigbluebutton/bigbluebutton-client;
|
||||
index index.html index.htm;
|
||||
expires 1m;
|
||||
}
|
||||
|
||||
# BigBlueButton Flash client.
|
||||
location /client {
|
||||
root /home/firstuser/dev/bigbluebutton/bigbluebutton-client;
|
||||
index index.html index.htm;
|
||||
}
|
||||
" | sudo tee /etc/bigbluebutton/nginx/client_dev
|
||||
```
|
||||
|
||||
Check the contents to ensure it matches below.
|
||||
|
||||
Again, make sure you change `/home/firstuser` to match your home directory.
|
||||
|
||||
```
|
||||
$ cat /etc/bigbluebutton/nginx/client_dev
|
||||
|
||||
location /client/BigBlueButton.html {
|
||||
root /home/firstuser/dev/bigbluebutton/bigbluebutton-client;
|
||||
index index.html index.htm;
|
||||
expires 1m;
|
||||
}
|
||||
|
||||
# BigBlueButton Flash client.
|
||||
location /client {
|
||||
root /home/firstuser/dev/bigbluebutton/bigbluebutton-client;
|
||||
index index.html index.htm;
|
||||
}
|
||||
```
|
||||
|
||||
These rules tell nginx where to find the BigBlueButton client. Currently, nginx is using the rules with the default BigBlueButton client through a symbolic link.
|
||||
|
||||
```
|
||||
$ ls -al /etc/bigbluebutton/nginx/client.nginx
|
||||
lrwxrwxrwx 1 root root 31 2013-05-05 15:44 /etc/bigbluebutton/nginx/client.nginx -> /etc/bigbluebutton/nginx/client
|
||||
```
|
||||
|
||||
Modify this symbolic link so it points to the development directory for your BigBlueButton client.
|
||||
|
||||
```
|
||||
sudo ln -f -s /etc/bigbluebutton/nginx/client_dev /etc/bigbluebutton/nginx/client.nginx
|
||||
```
|
||||
|
||||
Check that the modifications are in place.
|
||||
|
||||
```
|
||||
$ ls -al /etc/bigbluebutton/nginx/client.nginx
|
||||
lrwxrwxrwx 1 root root 35 2013-05-05 21:07 /etc/bigbluebutton/nginx/client.nginx -> /etc/bigbluebutton/nginx/client_dev
|
||||
```
|
||||
|
||||
Now we need to restart nginx so our changes take effect.
|
||||
|
||||
```
|
||||
$ sudo service nginx restart
|
||||
Restarting nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
|
||||
configuration file /etc/nginx/nginx.conf test is successful nginx.
|
||||
```
|
||||
|
||||
Now, when you launch the BigBlueButton client, nginx will serve the client from your development directory. Next, we need to rebuild the client.
|
||||
|
||||
## Building the client
|
||||
Let's now build the client. Note we're going to build and run the client to make sure it works before making any changes to the source.
|
||||
|
||||
First, we'll build the locales (language translation files). If you are not modifying the locales, you only need to do this once.
|
||||
|
||||
```
|
||||
cd ~/dev/bigbluebutton/bigbluebutton-client
|
||||
```
|
||||
|
||||
Build build a specific locale (en_US default)
|
||||
|
||||
```
|
||||
ant locale -DLOCALE=en_US
|
||||
```
|
||||
|
||||
To build all locales
|
||||
|
||||
```
|
||||
ant locales
|
||||
```
|
||||
|
||||
This will take about 10 minutes (depending on the speed of your computer). Next, let's build the client
|
||||
|
||||
```
|
||||
ant
|
||||
```
|
||||
|
||||
This will create a build of the BigBlueButton client in the `/home/firstuser/dev/bigbluebutton/bigbluebutton-client/client` directory.
|
||||
|
||||
|
||||
## Setup nginx
|
||||
|
||||
Create file `/etc/bigbluebutton/nginx/screenshare.nginx` and add the following:
|
||||
|
||||
```
|
||||
# Handle desktop sharing. Forwards
|
||||
# requests to Red5 on port 5080.
|
||||
location /screenshare {
|
||||
proxy_pass http://127.0.0.1:5080;
|
||||
proxy_redirect default;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
client_max_body_size 10m;
|
||||
client_body_buffer_size 128k;
|
||||
proxy_connect_timeout 90;
|
||||
proxy_send_timeout 90;
|
||||
proxy_read_timeout 90;
|
||||
proxy_buffer_size 4k;
|
||||
proxy_buffers 4 32k;
|
||||
proxy_busy_buffers_size 64k;
|
||||
proxy_temp_file_write_size 64k;
|
||||
include fastcgi_params;
|
||||
}
|
||||
```
|
||||
|
||||
Restart nginx
|
||||
|
||||
```
|
||||
sudo service nginx restart
|
||||
```
|
||||
|
||||
## Build BBB Red5 Applications
|
||||
|
||||
Turn off red5 service
|
||||
|
||||
```
|
||||
sudo service bbb-red5 stop
|
||||
```
|
||||
|
||||
You need to make `red5/webapps` writeable. Otherwise, you will get a permission error when you try to deploy into Red5.
|
||||
|
||||
```
|
||||
sudo chmod -R 777 /usr/share/red5/webapps
|
||||
```
|
||||
|
||||
### Build common-message
|
||||
|
||||
```
|
||||
cd ~/dev/bigbluebutton/bbb-common-message/
|
||||
sbt publish
|
||||
sbt publishLocal
|
||||
```
|
||||
|
||||
### Build bbb-apps
|
||||
|
||||
```
|
||||
cd ~/dev/bigbluebutton/bigbluebutton-apps/
|
||||
gradle resolveDeps
|
||||
gradle clean war deploy
|
||||
```
|
||||
|
||||
### Build bbb-voice
|
||||
|
||||
```
|
||||
cd ~/dev/bigbluebutton/bbb-voice
|
||||
```
|
||||
|
||||
Edit `src/main/webapp/WEB-INF/bigbluebutton-sip.properties`
|
||||
Make sure the IP addresses point to yout BBB server.
|
||||
|
||||
```
|
||||
bbb.sip.app.ip=192.168.74.128
|
||||
|
||||
# The ip and port of the FreeSWITCH server
|
||||
freeswitch.ip=192.168.74.128
|
||||
```
|
||||
|
||||
```
|
||||
gradle resolveDeps
|
||||
gradle clean war deploy
|
||||
```
|
||||
|
||||
### Build bbb-video
|
||||
|
||||
```
|
||||
cd ~/dev/bigbluebutton/bbb-video/
|
||||
gradle resolveDeps
|
||||
gradle clean war deploy
|
||||
```
|
||||
|
||||
### Build bbb-screenshare
|
||||
|
||||
```
|
||||
cd ~/dev/bigbluebutton/bbb-screenshare/app
|
||||
```
|
||||
|
||||
Edit `src/main/webapp/WEB-INF/screenshare.properties`
|
||||
Make sure the following points to your BBB server.
|
||||
|
||||
```
|
||||
streamBaseUrl=rtmp://192.168.74.128/screenshare
|
||||
jnlpUrl=http://192.168.74.128/screenshare
|
||||
jnlpFile=http://192.168.74.128/screenshare/screenshare.jnlp
|
||||
```
|
||||
|
||||
Build and deploy
|
||||
|
||||
```
|
||||
./deploy.sh
|
||||
```
|
||||
|
||||
## Stop services
|
||||
|
||||
|
||||
```
|
||||
sudo /etc/init.d/bbb-red5 stop
|
||||
sudo service bbb-apps-akka stop
|
||||
sudo service bbb-fsesl-akka stop
|
||||
```
|
||||
|
||||
Remove old `bbb-web` app from tomcat
|
||||
|
||||
```
|
||||
sudo rm /var/lib/tomcat7/webapps/bigbluebutton.war
|
||||
```
|
||||
|
||||
## Manually start services
|
||||
|
||||
### Run Red5
|
||||
|
||||
Open up a terminal.
|
||||
|
||||
```
|
||||
cd /usr/share/red5
|
||||
sudo -u red5 ./red5.sh
|
||||
```
|
||||
|
||||
### Run Akka Apps
|
||||
|
||||
Open up another terminal.
|
||||
|
||||
```
|
||||
cd ~/dev/bigbluebutton/akka-bbb-apps
|
||||
sbt run
|
||||
```
|
||||
|
||||
### Run Akka FSESL App
|
||||
|
||||
Open another terminal
|
||||
|
||||
```
|
||||
cd ~/dev/bigbluebutton/akka-bbb-fsesl
|
||||
sbt run
|
||||
```
|
||||
|
||||
### Run bbb-web
|
||||
|
||||
```
|
||||
cd ~/dev/bigbluebutton/bigbluebutton-web
|
||||
```
|
||||
|
||||
Get the salt and BBB URL from `/var/lib/tomcat7/webapps/demo/bbb_api_conf.jsp`
|
||||
|
||||
Edit `grails-app/conf/bigbluebutton.properties` and change the following with
|
||||
the salt and IP you got from above.
|
||||
|
||||
```
|
||||
bigbluebutton.web.serverURL=http://192.168.74.128
|
||||
securitySalt=856d5e0197b1aa0cf79897841142a5f6
|
||||
```
|
||||
|
||||
Start bbb-web
|
||||
|
||||
```
|
||||
gradle resolveDeps
|
||||
grails clean
|
||||
grails -Dserver.port=8888 run-war
|
||||
```
|
||||
|
||||
If things started without errors, congrats!
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -6,7 +6,7 @@ organization := "org.bigbluebutton"
|
||||
|
||||
version := "0.0.2"
|
||||
|
||||
scalaVersion := "2.11.6"
|
||||
scalaVersion := "2.11.7"
|
||||
|
||||
scalacOptions ++= Seq(
|
||||
"-unchecked",
|
||||
@ -38,22 +38,32 @@ testOptions in Test += Tests.Argument(TestFrameworks.Specs2, "html", "console",
|
||||
testOptions in Test += Tests.Argument(TestFrameworks.ScalaTest, "-h", "target/scalatest-reports")
|
||||
|
||||
libraryDependencies ++= {
|
||||
val akkaVersion = "2.3.11"
|
||||
val akkaVersion = "2.3.14"
|
||||
val akkaStreamV = "1.0"
|
||||
val scalaTestV = "2.2.4"
|
||||
Seq(
|
||||
"com.typesafe.akka" %% "akka-actor" % akkaVersion,
|
||||
"com.typesafe.akka" %% "akka-testkit" % akkaVersion % "test",
|
||||
"com.typesafe.akka" %% "akka-slf4j" % akkaVersion,
|
||||
"ch.qos.logback" % "logback-classic" % "1.0.13" % "runtime",
|
||||
"org.pegdown" % "pegdown" % "1.4.0",
|
||||
"junit" % "junit" % "4.11",
|
||||
"com.etaty.rediscala" %% "rediscala" % "1.4.0",
|
||||
"commons-codec" % "commons-codec" % "1.8",
|
||||
"joda-time" % "joda-time" % "2.3",
|
||||
"com.google.code.gson" % "gson" % "1.7.1",
|
||||
"redis.clients" % "jedis" % "2.7.2",
|
||||
"org.apache.commons" % "commons-lang3" % "3.2",
|
||||
"org.bigbluebutton" % "bbb-common-message" % "0.0.17"
|
||||
)}
|
||||
"com.typesafe.akka" %% "akka-actor" % akkaVersion,
|
||||
"com.typesafe.akka" %% "akka-testkit" % akkaVersion % "test",
|
||||
"com.typesafe.akka" %% "akka-slf4j" % akkaVersion,
|
||||
"com.typesafe.akka" %% "akka-stream-experimental" % akkaStreamV,
|
||||
"com.typesafe.akka" %% "akka-http-core-experimental" % akkaStreamV,
|
||||
"com.typesafe.akka" %% "akka-http-experimental" % akkaStreamV,
|
||||
"com.typesafe.akka" %% "akka-http-spray-json-experimental" % akkaStreamV,
|
||||
"com.typesafe.akka" %% "akka-http-testkit-experimental" % akkaStreamV,
|
||||
"org.scalatest" % "scalatest_2.11" % scalaTestV % "test",
|
||||
"ch.qos.logback" % "logback-classic" % "1.0.13" % "runtime",
|
||||
"org.pegdown" % "pegdown" % "1.4.0",
|
||||
"junit" % "junit" % "4.11",
|
||||
"com.etaty.rediscala" %% "rediscala" % "1.4.0",
|
||||
"commons-codec" % "commons-codec" % "1.8",
|
||||
"joda-time" % "joda-time" % "2.3",
|
||||
"com.google.code.gson" % "gson" % "2.5",
|
||||
"redis.clients" % "jedis" % "2.7.2",
|
||||
"org.apache.commons" % "commons-lang3" % "3.2",
|
||||
"org.bigbluebutton" % "bbb-common-message" % "0.0.18-SNAPSHOT",
|
||||
"io.spray" %% "spray-json" % "1.3.2"
|
||||
)
|
||||
}
|
||||
|
||||
seq(Revolver.settings: _*)
|
||||
|
||||
|
32
akka-bbb-apps/scala/BreakoutRoom.sc
Normal file
32
akka-bbb-apps/scala/BreakoutRoom.sc
Normal file
@ -0,0 +1,32 @@
|
||||
import org.bigbluebutton.core.apps.BreakoutRoomModel
|
||||
import org.bigbluebutton.core.apps.BreakoutRoomApp
|
||||
|
||||
object BreakoutRoom {
|
||||
val breakoutModel = new BreakoutRoomModel //> breakoutModel : org.bigbluebutton.core.apps.BreakoutRoomModel = org.bigblue
|
||||
//| button.core.apps.BreakoutRoomModel@721d4bd9
|
||||
|
||||
breakoutModel.createBreakoutRoom("1", "Room 1", "voice-1", Vector("user-1"), "default.pdf")
|
||||
//> res0: org.bigbluebutton.core.apps.BreakoutRoom = BreakoutRoom(1,Room 1,voice
|
||||
//| -1,Vector(user-1),Vector(),default.pdf)
|
||||
breakoutModel.createBreakoutRoom("2", "Room 2", "voice-2", Vector("user-2"), "default.pdf")
|
||||
//> res1: org.bigbluebutton.core.apps.BreakoutRoom = BreakoutRoom(2,Room 2,voice
|
||||
//| -2,Vector(user-2),Vector(),default.pdf)
|
||||
breakoutModel.getAssignedUsers("1") //> res2: Option[Vector[String]] = Some(Vector(user-1))
|
||||
breakoutModel.getAssignedUsers("2") //> res3: Option[Vector[String]] = Some(Vector(user-2))
|
||||
|
||||
var breakoutRoomId = "1" //> breakoutRoomId : String = 1
|
||||
|
||||
breakoutModel.getAssignedUsers(breakoutRoomId) foreach { users =>
|
||||
users.foreach { u =>
|
||||
println(Vector(u, breakoutRoomId))
|
||||
} //> Vector(user-1, 1)
|
||||
}
|
||||
|
||||
breakoutRoomId = "2"
|
||||
|
||||
breakoutModel.getAssignedUsers(breakoutRoomId) foreach { users =>
|
||||
users.foreach { u =>
|
||||
println(Vector(u, breakoutRoomId))
|
||||
} //> Vector(user-2, 2)
|
||||
}
|
||||
}
|
124
akka-bbb-apps/scala/JsonTest.sc
Executable file
124
akka-bbb-apps/scala/JsonTest.sc
Executable file
@ -0,0 +1,124 @@
|
||||
import org.bigbluebutton.core.api._
|
||||
import scala.util.{ Try, Success, Failure }
|
||||
import org.bigbluebutton.core.JsonMessageDecoder
|
||||
import org.bigbluebutton.messages.BreakoutRoomsList
|
||||
import org.bigbluebutton.messages.payload.BreakoutRoomsListPayload
|
||||
import java.util.ArrayList
|
||||
import org.bigbluebutton.core.messaging.Util
|
||||
import org.bigbluebutton.messages.payload.BreakoutRoomPayload
|
||||
import org.bigbluebutton.core.pubsub.senders.MeetingMessageToJsonConverter
|
||||
import spray.json._
|
||||
import DefaultJsonProtocol._
|
||||
import com.google.gson.JsonArray
|
||||
import scala.collection.mutable.ListBuffer
|
||||
|
||||
object JsonTest {
|
||||
import org.bigbluebutton.core.UserMessagesProtocol._
|
||||
import spray.json._
|
||||
|
||||
println("Welcome to the Scala worksheet") //> Welcome to the Scala worksheet
|
||||
|
||||
val xroom1 = new BreakoutRoomInPayload("foo", Vector("a", "b", "c"))
|
||||
//> xroom1 : org.bigbluebutton.core.api.BreakoutRoomInPayload = BreakoutRoomInP
|
||||
//| ayload(foo,Vector(a, b, c))
|
||||
val xroom2 = new BreakoutRoomInPayload("bar", Vector("x", "y", "z"))
|
||||
//> xroom2 : org.bigbluebutton.core.api.BreakoutRoomInPayload = BreakoutRoomInP
|
||||
//| ayload(bar,Vector(x, y, z))
|
||||
val xroom3 = new BreakoutRoomInPayload("baz", Vector("q", "r", "s"))
|
||||
//> xroom3 : org.bigbluebutton.core.api.BreakoutRoomInPayload = BreakoutRoomInP
|
||||
//| ayload(baz,Vector(q, r, s))
|
||||
|
||||
val xmsg = new CreateBreakoutRooms("test-meeting", 10, Vector(xroom1, xroom2, xroom3))
|
||||
//> xmsg : org.bigbluebutton.core.api.CreateBreakoutRooms = CreateBreakoutRoom
|
||||
//| s(test-meeting,10,Vector(BreakoutRoomInPayload(foo,Vector(a, b, c)), Breako
|
||||
//| utRoomInPayload(bar,Vector(x, y, z)), BreakoutRoomInPayload(baz,Vector(q, r
|
||||
//| , s))))
|
||||
|
||||
val xjsonAst = xmsg.toJson //> xjsonAst : spray.json.JsValue = {"meetingId":"test-meeting","durationInMin
|
||||
//| utes":10,"rooms":[{"name":"foo","users":["a","b","c"]},{"name":"bar","users
|
||||
//| ":["x","y","z"]},{"name":"baz","users":["q","r","s"]}]}
|
||||
val xjson = xjsonAst.asJsObject //> xjson : spray.json.JsObject = {"meetingId":"test-meeting","durationInMinut
|
||||
//| es":10,"rooms":[{"name":"foo","users":["a","b","c"]},{"name":"bar","users":
|
||||
//| ["x","y","z"]},{"name":"baz","users":["q","r","s"]}]}
|
||||
val meetingId = for {
|
||||
meetingId <- xjson.fields.get("meetingId")
|
||||
} yield meetingId //> meetingId : Option[spray.json.JsValue] = Some("test-meeting")
|
||||
|
||||
val cbrm = """
|
||||
{"header":{"name":"CreateBreakoutRoomsRequest"},"payload":{"meetingId":"abc123","rooms":[{"name":"room1","users":["Tidora","Nidora","Tinidora"]},{"name":"room2","users":["Jose","Wally","Paolo"]},{"name":"room3","users":["Alden","Yaya Dub"]}],"durationInMinutes":20}}
|
||||
""" //> cbrm : String = "
|
||||
//| {"header":{"name":"CreateBreakoutRoomsRequest"},"payload":{"meetingId":"a
|
||||
//| bc123","rooms":[{"name":"room1","users":["Tidora","Nidora","Tinidora"]},{"n
|
||||
//| ame":"room2","users":["Jose","Wally","Paolo"]},{"name":"room3","users":["Al
|
||||
//| den","Yaya Dub"]}],"durationInMinutes":20}}
|
||||
//| "
|
||||
|
||||
JsonMessageDecoder.decode(cbrm) //> res0: Option[org.bigbluebutton.core.api.InMessage] = Some(CreateBreakoutRoo
|
||||
//| ms(abc123,20,Vector(BreakoutRoomInPayload(room1,Vector(Tidora, Nidora, Tini
|
||||
//| dora)), BreakoutRoomInPayload(room2,Vector(Jose, Wally, Paolo)), BreakoutRo
|
||||
//| omInPayload(room3,Vector(Alden, Yaya Dub)))))
|
||||
val rbju = """
|
||||
{"header":{"name":"RequestBreakoutJoinURL"},"payload":{"userId":"id6pa5t8m1c9_1","meetingId":"183f0bf3a0982a127bdb8161e0c44eb696b3e75c-1452692983357","breakoutId":"183f0bf3a0982a127bdb8161e0c44eb696b3e75c-1452692983357-2"}}
|
||||
""" //> rbju : String = "
|
||||
//| {"header":{"name":"RequestBreakoutJoinURL"},"payload":{"userId":"id6pa5t8
|
||||
//| m1c9_1","meetingId":"183f0bf3a0982a127bdb8161e0c44eb696b3e75c-1452692983357
|
||||
//| ","breakoutId":"183f0bf3a0982a127bdb8161e0c44eb696b3e75c-1452692983357-2"}}
|
||||
//|
|
||||
//| "
|
||||
|
||||
JsonMessageDecoder.decode(rbju) //> res1: Option[org.bigbluebutton.core.api.InMessage] = Some(RequestBreakoutJo
|
||||
//| inURLInMessage(183f0bf3a0982a127bdb8161e0c44eb696b3e75c-1452692983357,183f0
|
||||
//| bf3a0982a127bdb8161e0c44eb696b3e75c-1452692983357-2,id6pa5t8m1c9_1))
|
||||
|
||||
val brl = """
|
||||
{"payload":{"meetingId":"183f0bf3a0982a127bdb8161e0c44eb696b3e75c-1452849036428","rooms":{"startIndex":0,"endIndex":0,"focus":0,"dirty":false,"depth":0}},"header":{"timestamp":33619724,"name":"BreakoutRoomsList","current_time":1452849043547,"version":"0.0.1"}}
|
||||
""" //> brl : String = "
|
||||
//| {"payload":{"meetingId":"183f0bf3a0982a127bdb8161e0c44eb696b3e75c-1452849
|
||||
//| 036428","rooms":{"startIndex":0,"endIndex":0,"focus":0,"dirty":false,"depth
|
||||
//| ":0}},"header":{"timestamp":33619724,"name":"BreakoutRoomsList","current_ti
|
||||
//| me":1452849043547,"version":"0.0.1"}}
|
||||
//| "
|
||||
|
||||
val brb = new BreakoutRoomBody("Breakout Room", "br-id-1");
|
||||
//> brb : org.bigbluebutton.core.api.BreakoutRoomBody = BreakoutRoomBody(Break
|
||||
//| out Room,br-id-1)
|
||||
|
||||
val jsObj = JsObject(
|
||||
"name" -> JsString(brb.name),
|
||||
"breakoutId" -> JsString(brb.breakoutId)) //> jsObj : spray.json.JsObject = {"name":"Breakout Room","breakoutId":"br-id-
|
||||
//| 1"}
|
||||
|
||||
val vector = Vector(jsObj) //> vector : scala.collection.immutable.Vector[spray.json.JsObject] = Vector({
|
||||
//| "name":"Breakout Room","breakoutId":"br-id-1"})
|
||||
|
||||
val brlum = new BreakoutRoomsListOutMessage("main-meeting-1", Vector(brb))
|
||||
//> brlum : org.bigbluebutton.core.api.BreakoutRoomsListOutMessage = BreakoutR
|
||||
//| oomsListOutMessage(main-meeting-1,Vector(BreakoutRoomBody(Breakout Room,br-
|
||||
//| id-1)))
|
||||
var roomsJsVector: ListBuffer[JsObject] = new ListBuffer[JsObject]()
|
||||
//> roomsJsVector : scala.collection.mutable.ListBuffer[spray.json.JsObject] =
|
||||
//| ListBuffer()
|
||||
brlum.rooms.foreach { r =>
|
||||
roomsJsVector.append(JsObject("name" -> JsString(r.name), "breakoutId" -> JsString(r.breakoutId)))
|
||||
}
|
||||
|
||||
roomsJsVector.length //> res2: Int = 1
|
||||
|
||||
val jsonAst = List(1, 2, 3).toJson //> jsonAst : spray.json.JsValue = [1,2,3]
|
||||
|
||||
roomsJsVector.toList.toJson //> res3: spray.json.JsValue = [{"name":"Breakout Room","breakoutId":"br-id-1"}
|
||||
//| ]
|
||||
JsArray(roomsJsVector.toVector) //> res4: spray.json.JsArray = [{"name":"Breakout Room","breakoutId":"br-id-1"}
|
||||
//| ]
|
||||
|
||||
MeetingMessageToJsonConverter.breakoutRoomsListOutMessageToJson(brlum);
|
||||
//> res5: String = {"payload":{"meetingId":"main-meeting-1","rooms":[{"name":"B
|
||||
//| reakout Room","breakoutId":"br-id-1"}]},"header":{"timestamp":47333494,"nam
|
||||
//| e":"BreakoutRoomsList","current_time":1453399680291,"version":"0.0.1"}}
|
||||
|
||||
// JsonMessageDecoder.unmarshall(cbrm) match {
|
||||
// case Success(validMsg) => println(validMsg)
|
||||
// case Failure(ex) => println("Unhandled message: [{}]")
|
||||
// }
|
||||
|
||||
}
|
67
akka-bbb-apps/scala/Test2.sc
Executable file
67
akka-bbb-apps/scala/Test2.sc
Executable file
@ -0,0 +1,67 @@
|
||||
import scala.collection.immutable.StringOps
|
||||
import java.net.URLEncoder
|
||||
import scala.collection._
|
||||
|
||||
object Test2 {
|
||||
println("Welcome to the Scala worksheet") //> Welcome to the Scala worksheet
|
||||
|
||||
val userId = new StringOps("abc_12") //> userId : scala.collection.immutable.StringOps = abc_12
|
||||
val s2 = userId.split('_') //> s2 : Array[String] = Array(abc, 12)
|
||||
val s1 = if (s2.length == 2) s2(0) else userId //> s1 : Comparable[String] = abc
|
||||
|
||||
def sortParam(params: mutable.Map[String, String]):SortedSet[String] = {
|
||||
collection.immutable.SortedSet[String]() ++ params.keySet
|
||||
} //> sortParam: (params: scala.collection.mutable.Map[String,String])scala.collec
|
||||
//| tion.SortedSet[String]
|
||||
|
||||
def createBaseString(params: mutable.Map[String, String]): String = {
|
||||
val csbuf = new StringBuffer()
|
||||
var keys = sortParam(params)
|
||||
|
||||
var first = true;
|
||||
for (key <- keys) {
|
||||
for (value <- params.get(key)) {
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
csbuf.append("&");
|
||||
}
|
||||
|
||||
csbuf.append(key);
|
||||
csbuf.append("=");
|
||||
csbuf.append(value);
|
||||
}
|
||||
}
|
||||
|
||||
return csbuf.toString();
|
||||
} //> createBaseString: (params: scala.collection.mutable.Map[String,String])Strin
|
||||
//| g
|
||||
|
||||
def urlEncode(s: String): String = {
|
||||
URLEncoder.encode(s, "UTF-8");
|
||||
} //> urlEncode: (s: String)String
|
||||
|
||||
val baseString = "fullName=User+4621018&isBreakout=true&meetingID=random-1853792&password=mp&redirect=true"
|
||||
//> baseString : String = fullName=User+4621018&isBreakout=true&meetingID=rand
|
||||
//| om-1853792&password=mp&redirect=true
|
||||
|
||||
val params = new collection.mutable.HashMap[String, String]
|
||||
//> params : scala.collection.mutable.HashMap[String,String] = Map()
|
||||
params += "fullName" -> urlEncode("User 4621018")
|
||||
//> res0: Test2.params.type = Map(fullName -> User+4621018)
|
||||
params += "isBreakout" -> urlEncode("true") //> res1: Test2.params.type = Map(fullName -> User+4621018, isBreakout -> true)
|
||||
//|
|
||||
params += "meetingID" -> urlEncode("random-1853792")
|
||||
//> res2: Test2.params.type = Map(fullName -> User+4621018, isBreakout -> true,
|
||||
//| meetingID -> random-1853792)
|
||||
params += "password" -> urlEncode("mp") //> res3: Test2.params.type = Map(fullName -> User+4621018, isBreakout -> true,
|
||||
//| meetingID -> random-1853792, password -> mp)
|
||||
params += "redirect" -> urlEncode("true") //> res4: Test2.params.type = Map(fullName -> User+4621018, isBreakout -> true,
|
||||
//| meetingID -> random-1853792, redirect -> true, password -> mp)
|
||||
val keys = sortParam(params) //> keys : scala.collection.SortedSet[String] = TreeSet(fullName, isBreakout,
|
||||
//| meetingID, password, redirect)
|
||||
|
||||
val result = createBaseString(params) //> result : String = fullName=User+4621018&isBreakout=true&meetingID=random-1
|
||||
//| 853792&password=mp&redirect=true
|
||||
|
||||
}
|
126
akka-bbb-apps/scala/TestWorksheet.sc
Executable file
126
akka-bbb-apps/scala/TestWorksheet.sc
Executable file
@ -0,0 +1,126 @@
|
||||
import scala.collection.immutable.StringOps
|
||||
import org.bigbluebutton.core.apps.Answer
|
||||
import org.bigbluebutton.core.apps.Question
|
||||
|
||||
object TestWorksheet {
|
||||
println("Welcome to the Scala worksheet") //> Welcome to the Scala worksheet
|
||||
|
||||
|
||||
val YesNoPollType = "YN" //> YesNoPollType : String = YN
|
||||
val TrueFalsePollType = "TF" //> TrueFalsePollType : String = TF
|
||||
val LetterPollType = "A-" //> LetterPollType : String = A-
|
||||
val NumberPollType = "1-" //> NumberPollType : String = 1-
|
||||
|
||||
val LetterArray = Array("A", "B", "C", "D", "E", "F")
|
||||
//> LetterArray : Array[String] = Array(A, B, C, D, E, F)
|
||||
val NumberArray = Array("1", "2", "3", "4", "5", "6")
|
||||
//> NumberArray : Array[String] = Array(1, 2, 3, 4, 5, 6)
|
||||
|
||||
def processYesNoPollType(qType: String):Question = {
|
||||
val answers = new Array[Answer](2)
|
||||
answers(0) = new Answer(0, "N", Some("No"))
|
||||
answers(1) = new Answer(1, "Y", Some("Yes"))
|
||||
|
||||
new Question(0, YesNoPollType, false, None, answers)
|
||||
} //> processYesNoPollType: (qType: String)org.bigbluebutton.core.apps.Question
|
||||
|
||||
def processTrueFalsePollType(qType: String):Question = {
|
||||
val answers = new Array[Answer](2)
|
||||
|
||||
answers(0) = new Answer(0, "F", Some("False"))
|
||||
answers(1) = new Answer(1, "T", Some("True"))
|
||||
|
||||
new Question(0, TrueFalsePollType, false, None, answers)
|
||||
} //> processTrueFalsePollType: (qType: String)org.bigbluebutton.core.apps.Questio
|
||||
//| n
|
||||
|
||||
|
||||
def processLetterPollType(qType: String, multiResponse: Boolean):Option[Question] = {
|
||||
val q = qType.split('-')
|
||||
val numQs = q(1).toInt
|
||||
|
||||
var questionOption: Option[Question] = None
|
||||
|
||||
if (numQs > 0 && numQs <= 7) {
|
||||
val answers = new Array[Answer](numQs)
|
||||
var i = 0
|
||||
for ( i <- 0 until numQs ) {
|
||||
answers(i) = new Answer(i, LetterArray(i), Some(LetterArray(i)))
|
||||
val question = new Question(0, LetterPollType, multiResponse, None, answers)
|
||||
questionOption = Some(question)
|
||||
}
|
||||
}
|
||||
|
||||
questionOption
|
||||
} //> processLetterPollType: (qType: String, multiResponse: Boolean)Option[org.bi
|
||||
//| gbluebutton.core.apps.Question]
|
||||
|
||||
def processNumberPollType(qType: String, multiResponse: Boolean):Option[Question] = {
|
||||
val q = qType.split('-')
|
||||
val numQs = q(1).toInt
|
||||
|
||||
var questionOption: Option[Question] = None
|
||||
|
||||
if (numQs > 0 && numQs <= 7) {
|
||||
val answers = new Array[Answer](numQs)
|
||||
var i = 0
|
||||
for ( i <- 0 until numQs ) {
|
||||
answers(i) = new Answer(i, NumberArray(i), Some(NumberArray(i)))
|
||||
val question = new Question(0, NumberPollType, multiResponse, None, answers)
|
||||
questionOption = Some(question)
|
||||
}
|
||||
}
|
||||
questionOption
|
||||
} //> processNumberPollType: (qType: String, multiResponse: Boolean)Option[org.bi
|
||||
//| gbluebutton.core.apps.Question]
|
||||
|
||||
def createQuestion(qType: String):Option[Question] = {
|
||||
val qt = qType.toUpperCase()
|
||||
var questionOption: Option[Question] = None
|
||||
if (qt.matches(YesNoPollType)) {
|
||||
questionOption = Some(processYesNoPollType(qt))
|
||||
} else if (qt.matches(TrueFalsePollType)) {
|
||||
questionOption = Some(processTrueFalsePollType(qt))
|
||||
} else if (qt.startsWith(LetterPollType)) {
|
||||
questionOption = processLetterPollType(qt, false)
|
||||
} else if (qt.startsWith(NumberPollType)) {
|
||||
processNumberPollType(qt, false)
|
||||
} else {
|
||||
questionOption = None
|
||||
}
|
||||
|
||||
questionOption
|
||||
} //> createQuestion: (qType: String)Option[org.bigbluebutton.core.apps.Question]
|
||||
//|
|
||||
|
||||
def determineQType3(qType: String) {
|
||||
val qt = qType.toUpperCase()
|
||||
|
||||
if (qt.matches(YesNoPollType)) {
|
||||
println("YN")
|
||||
} else if (qt.matches("TF")) {
|
||||
println(TrueFalsePollType)
|
||||
} else if (qt.startsWith(LetterPollType)) {
|
||||
println("A5")
|
||||
processLetterPollType(qt, false)
|
||||
} else if (qt.startsWith(NumberPollType)) {
|
||||
println("1")
|
||||
processNumberPollType(qt, false)
|
||||
} else {
|
||||
println("No Match for [" + qType + "]")
|
||||
}
|
||||
} //> determineQType3: (qType: String)Unit
|
||||
|
||||
determineQType3("YN") //> YN
|
||||
determineQType3("YF") //> No Match for [YF]
|
||||
determineQType3("TF") //> TF
|
||||
determineQType3("A-5") //> A5
|
||||
determineQType3("1-5") //> 1
|
||||
|
||||
val list = new java.util.ArrayList[String]() //> list : java.util.ArrayList[String] = []
|
||||
list.add("Red") //> res0: Boolean = true
|
||||
list.add("Green") //> res1: Boolean = true
|
||||
list.add("Blue") //> res2: Boolean = true
|
||||
|
||||
|
||||
}
|
@ -5,33 +5,30 @@ import org.bigbluebutton.common.messages.*;
|
||||
|
||||
public interface IBigBlueButtonInGW {
|
||||
|
||||
void handleJsonMessage(String json);
|
||||
void handleBigBlueButtonMessage(IBigBlueButtonMessage message);
|
||||
|
||||
|
||||
void isAliveAudit(String aliveID);
|
||||
void statusMeetingAudit(String meetingID);
|
||||
void endMeeting(String meetingID);
|
||||
void endMeeting(String meetingId);
|
||||
void endAllMeetings();
|
||||
void createMeeting2(String meetingID, String externalMeetingID, String meetingName, boolean recorded,
|
||||
String voiceBridge, long duration, boolean autoStartRecording,
|
||||
boolean allowStartStopRecording, String moderatorPass, String viewerPass,
|
||||
long createTime, String createDate);
|
||||
|
||||
void destroyMeeting(String meetingID);
|
||||
void getAllMeetings(String meetingID);
|
||||
void lockSettings(String meetingID, Boolean locked, Map<String, Boolean> lockSettigs);
|
||||
|
||||
|
||||
// Polling
|
||||
void votePoll(String meetingId, String userId, String pollId, Integer questionId, Integer answerId);
|
||||
void startPoll(String meetingId, String requesterId, String pollId, String pollType);
|
||||
void stopPoll(String meetingId, String userId, String pollId);
|
||||
void showPollResult(String meetingId, String requesterId, String pollId, Boolean show);
|
||||
|
||||
|
||||
// Lock
|
||||
void initLockSettings(String meetingID, Map<String, Boolean> settings);
|
||||
void sendLockSettings(String meetingID, String userId, Map<String, Boolean> settings);
|
||||
void getLockSettings(String meetingId, String userId);
|
||||
void lockUser(String meetingId, String requesterID, boolean lock, String internalUserID);
|
||||
|
||||
|
||||
|
||||
// Users
|
||||
void validateAuthToken(String meetingId, String userId, String token, String correlationId, String sessionId);
|
||||
void registerUser(String roomName, String userid, String username, String role, String externUserID, String authToken, String avatarURL);
|
||||
@ -48,7 +45,7 @@ public interface IBigBlueButtonInGW {
|
||||
void getRecordingStatus(String meetingId, String userId);
|
||||
void userConnectedToGlobalAudio(String voiceConf, String userid, String name);
|
||||
void userDisconnectedFromGlobalAudio(String voiceConf, String userid, String name);
|
||||
|
||||
|
||||
// Voice
|
||||
void initAudioSettings(String meetingID, String requesterID, Boolean muted);
|
||||
void muteAllExceptPresenter(String meetingID, String requesterID, Boolean mute);
|
||||
@ -66,7 +63,7 @@ public interface IBigBlueButtonInGW {
|
||||
void voiceUserTalking(String meetingId, String userId, Boolean talking);
|
||||
void voiceRecording(String meetingId, String recordingFile,
|
||||
String timestamp, Boolean recording);
|
||||
|
||||
|
||||
// Presentation
|
||||
void clear(String meetingID);
|
||||
void removePresentation(String meetingID, String presentationID);
|
||||
@ -79,18 +76,18 @@ public interface IBigBlueButtonInGW {
|
||||
|
||||
void sendConversionUpdate(String messageKey, String meetingId,
|
||||
String code, String presId, String presName);
|
||||
|
||||
|
||||
void sendPageCountError(String messageKey, String meetingId,
|
||||
String code, String presId, int numberOfPages,
|
||||
int maxNumberPages, String presName);
|
||||
|
||||
|
||||
void sendSlideGenerated(String messageKey, String meetingId,
|
||||
String code, String presId, int numberOfPages,
|
||||
int pagesCompleted, String presName);
|
||||
|
||||
|
||||
void sendConversionCompleted(String messageKey, String meetingId,
|
||||
String code, String presId, int numPages, String presName, String presBaseUrl);
|
||||
|
||||
|
||||
// Layout
|
||||
void getCurrentLayout(String meetingID, String requesterID);
|
||||
void broadcastLayout(String meetingID, String requesterID, String layout);
|
||||
@ -110,6 +107,11 @@ public interface IBigBlueButtonInGW {
|
||||
void undoWhiteboard(String meetingID, String requesterID, String whiteboardId);
|
||||
void enableWhiteboard(String meetingID, String requesterID, Boolean enable);
|
||||
void isWhiteboardEnabled(String meetingID, String requesterID, String replyTo);
|
||||
|
||||
// Caption
|
||||
void sendCaptionHistory(String meetingID, String requesterID);
|
||||
void updateCaptionOwner(String meetingID, String locale, String ownerID);
|
||||
void editCaptionHistory(String meetingID, String userID, Integer startIndex, Integer endIndex, String locale, String text);
|
||||
|
||||
// DeskShare
|
||||
void deskShareStarted(String conferenceName, String callerId, String callerIdName);
|
||||
@ -117,5 +119,4 @@ public interface IBigBlueButtonInGW {
|
||||
void deskShareRTMPBroadcastStarted(String conferenceName, String streamname, int videoWidth, int videoHeight, String timestamp);
|
||||
void deskShareRTMPBroadcastStopped(String conferenceName, String streamname, int videoWidth, int videoHeight, String timestamp);
|
||||
void deskShareGetInfoRequest(String meetingId, String requesterId, String replyTo);
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,44 @@
|
||||
package org.bigbluebutton.core.pubsub.receivers;
|
||||
|
||||
import org.bigbluebutton.common.messages.MessagingConstants;
|
||||
import org.bigbluebutton.common.messages.EditCaptionHistoryMessage;
|
||||
import org.bigbluebutton.common.messages.SendCaptionHistoryRequestMessage;
|
||||
import org.bigbluebutton.common.messages.UpdateCaptionOwnerMessage;
|
||||
|
||||
import com.google.gson.JsonParser;
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
import org.bigbluebutton.core.api.IBigBlueButtonInGW;
|
||||
|
||||
public class CaptionMessageReceiver implements MessageHandler{
|
||||
|
||||
private IBigBlueButtonInGW bbbGW;
|
||||
|
||||
public CaptionMessageReceiver(IBigBlueButtonInGW bbbGW) {
|
||||
this.bbbGW = bbbGW;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMessage(String pattern, String channel, String message) {
|
||||
if (channel.equalsIgnoreCase(MessagingConstants.TO_CAPTION_CHANNEL)) {
|
||||
JsonParser parser = new JsonParser();
|
||||
JsonObject obj = (JsonObject) parser.parse(message);
|
||||
if (obj.has("header") && obj.has("payload")) {
|
||||
JsonObject header = (JsonObject) obj.get("header");
|
||||
if (header.has("name")) {
|
||||
String messageName = header.get("name").getAsString();
|
||||
if (SendCaptionHistoryRequestMessage.SEND_CAPTION_HISTORY_REQUEST.equals(messageName)){
|
||||
SendCaptionHistoryRequestMessage msg = SendCaptionHistoryRequestMessage.fromJson(message);
|
||||
bbbGW.sendCaptionHistory(msg.meetingID, msg.requesterID);
|
||||
} else if (UpdateCaptionOwnerMessage.UPDATE_CAPTION_OWNER.equals(messageName)) {
|
||||
UpdateCaptionOwnerMessage msg = UpdateCaptionOwnerMessage.fromJson(message);
|
||||
bbbGW.updateCaptionOwner(msg.meetingID, msg.locale, msg.ownerID);
|
||||
} else if (EditCaptionHistoryMessage.EDIT_CAPTION_HISTORY.equals(messageName)) {
|
||||
EditCaptionHistoryMessage msg = EditCaptionHistoryMessage.fromJson(message);
|
||||
bbbGW.editCaptionHistory(msg.meetingID, msg.userID, msg.startIndex, msg.endIndex, msg.locale, msg.text);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -3,7 +3,6 @@ package org.bigbluebutton.core.pubsub.receivers;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.bigbluebutton.common.messages.CreateMeetingMessage;
|
||||
import org.bigbluebutton.common.messages.DestroyMeetingMessage;
|
||||
import org.bigbluebutton.common.messages.EndMeetingMessage;
|
||||
import org.bigbluebutton.common.messages.GetAllMeetingsRequest;
|
||||
@ -17,6 +16,7 @@ import org.bigbluebutton.common.messages.UserConnectedToGlobalAudio;
|
||||
import org.bigbluebutton.common.messages.UserDisconnectedFromGlobalAudio;
|
||||
import org.bigbluebutton.common.messages.ValidateAuthTokenMessage;
|
||||
import org.bigbluebutton.core.api.IBigBlueButtonInGW;
|
||||
import org.bigbluebutton.messages.CreateMeetingRequest;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@ -34,20 +34,30 @@ public class MeetingMessageReceiver implements MessageHandler {
|
||||
}
|
||||
|
||||
public void handleMessage(String pattern, String channel, String message) {
|
||||
// LOG.debug("Checking message: " + pattern + " " + channel + " " + message);
|
||||
if (channel.equalsIgnoreCase(MessagingConstants.TO_MEETING_CHANNEL)) {
|
||||
// System.out.println("Meeting message: " + channel + " " + message);
|
||||
System.out.println("Meeting message: " + channel + " " + message);
|
||||
|
||||
JsonParser parser = new JsonParser();
|
||||
JsonObject obj = (JsonObject) parser.parse(message);
|
||||
if (obj.has("header") && obj.has("payload")) {
|
||||
JsonObject header = (JsonObject) obj.get("header");
|
||||
if (header.has("name")) {
|
||||
String messageName = header.get("name").getAsString();
|
||||
if (CreateMeetingRequest.NAME.equals(messageName)) {
|
||||
Gson gson = new Gson();
|
||||
CreateMeetingRequest msg = gson.fromJson(message,
|
||||
CreateMeetingRequest.class);
|
||||
bbbGW.handleBigBlueButtonMessage(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IBigBlueButtonMessage msg = MessageFromJsonConverter.convert(message);
|
||||
|
||||
if (msg != null) {
|
||||
if (msg instanceof EndMeetingMessage) {
|
||||
EndMeetingMessage emm = (EndMeetingMessage) msg;
|
||||
bbbGW.endMeeting(emm.meetingId);
|
||||
} else if (msg instanceof CreateMeetingMessage) {
|
||||
CreateMeetingMessage emm = (CreateMeetingMessage) msg;
|
||||
bbbGW.createMeeting2(emm.id, emm.externalId, emm.name, emm.record, emm.voiceBridge,
|
||||
emm.duration, emm.autoStartRecording, emm.allowStartStopRecording,
|
||||
emm.moderatorPass, emm.viewerPass, emm.createTime, emm.createDate);
|
||||
} else if (msg instanceof RegisterUserMessage) {
|
||||
RegisterUserMessage emm = (RegisterUserMessage) msg;
|
||||
bbbGW.registerUser(emm.meetingID, emm.internalUserId, emm.fullname, emm.role, emm.externUserID, emm.authToken, emm.avatarURL);
|
||||
|
@ -138,8 +138,7 @@ public class PresentationMessageListener implements MessageHandler {
|
||||
}
|
||||
}
|
||||
else {
|
||||
Gson gson = new Gson();
|
||||
HashMap<String,String> map = gson.fromJson(message, new TypeToken<Map<String, String>>() {}.getType());
|
||||
HashMap<String,String> map = new Gson().fromJson(message, new TypeToken<HashMap<String, String>>() {}.getType());
|
||||
String code = (String) map.get("returnCode");
|
||||
String presId = (String) map.get("presentationId");
|
||||
String filename = (String) map.get("filename");
|
||||
|
@ -54,6 +54,9 @@ public class RedisMessageReceiver {
|
||||
|
||||
MeetingMessageReceiver meetingRx = new MeetingMessageReceiver(bbbGW);
|
||||
receivers.add(meetingRx);
|
||||
|
||||
CaptionMessageReceiver captionRx = new CaptionMessageReceiver(bbbGW);
|
||||
receivers.add(captionRx);
|
||||
}
|
||||
|
||||
public void handleMessage(String pattern, String channel, String message) {
|
||||
|
@ -1,40 +1,17 @@
|
||||
|
||||
package org.bigbluebutton.core.pubsub.receivers;
|
||||
|
||||
import org.bigbluebutton.common.messages.AssignPresenterRequestMessage;
|
||||
import org.bigbluebutton.common.messages.BroadcastLayoutRequestMessage;
|
||||
import org.bigbluebutton.common.messages.EjectUserFromMeetingRequestMessage;
|
||||
import org.bigbluebutton.common.messages.EjectUserFromVoiceRequestMessage;
|
||||
import org.bigbluebutton.common.messages.GetCurrentLayoutRequestMessage;
|
||||
import org.bigbluebutton.common.messages.GetRecordingStatusRequestMessage;
|
||||
import org.bigbluebutton.common.messages.GetUsersRequestMessage;
|
||||
import org.bigbluebutton.common.messages.InitAudioSettingsMessage;
|
||||
import org.bigbluebutton.common.messages.InitPermissionsSettingMessage;
|
||||
import org.bigbluebutton.common.messages.IsMeetingMutedRequestMessage;
|
||||
import org.bigbluebutton.common.messages.LockLayoutRequestMessage;
|
||||
import org.bigbluebutton.common.messages.LockMuteUserRequestMessage;
|
||||
import org.bigbluebutton.common.messages.MessagingConstants;
|
||||
import org.bigbluebutton.common.messages.MuteAllExceptPresenterRequestMessage;
|
||||
import org.bigbluebutton.common.messages.MuteAllRequestMessage;
|
||||
import org.bigbluebutton.common.messages.MuteUserRequestMessage;
|
||||
import org.bigbluebutton.common.messages.SetRecordingStatusRequestMessage;
|
||||
import org.bigbluebutton.common.messages.SetUserStatusRequestMessage;
|
||||
import org.bigbluebutton.common.messages.UserJoinedVoiceConfMessage;
|
||||
import org.bigbluebutton.common.messages.UserLeavingMessage;
|
||||
import org.bigbluebutton.common.messages.UserLeftVoiceConfMessage;
|
||||
import org.bigbluebutton.common.messages.UserLockedInVoiceConfMessage;
|
||||
import org.bigbluebutton.common.messages.UserMutedInVoiceConfMessage;
|
||||
import org.bigbluebutton.common.messages.UserEmojiStatusMessage;
|
||||
import org.bigbluebutton.common.messages.UserShareWebcamRequestMessage;
|
||||
import org.bigbluebutton.common.messages.UserTalkingInVoiceConfMessage;
|
||||
import org.bigbluebutton.common.messages.UserUnshareWebcamRequestMessage;
|
||||
import org.bigbluebutton.common.messages.VoiceConfRecordingStartedMessage;
|
||||
import org.bigbluebutton.common.messages.*;
|
||||
import org.bigbluebutton.core.api.IBigBlueButtonInGW;
|
||||
import org.bigbluebutton.messages.*;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.gson.JsonParser;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
|
||||
public class UsersMessageReceiver implements MessageHandler{
|
||||
private static final Logger LOG = LoggerFactory.getLogger(UsersMessageReceiver.class);
|
||||
|
||||
private IBigBlueButtonInGW bbbInGW;
|
||||
|
||||
@ -45,7 +22,6 @@ public class UsersMessageReceiver implements MessageHandler{
|
||||
@Override
|
||||
public void handleMessage(String pattern, String channel, String message) {
|
||||
if (channel.equalsIgnoreCase(MessagingConstants.TO_USERS_CHANNEL)) {
|
||||
// System.out.println("Users message: " + channel + " " + message);
|
||||
JsonParser parser = new JsonParser();
|
||||
JsonObject obj = (JsonObject) parser.parse(message);
|
||||
if (obj.has("header") && obj.has("payload")) {
|
||||
@ -77,7 +53,7 @@ public class UsersMessageReceiver implements MessageHandler{
|
||||
break;
|
||||
case SetRecordingStatusRequestMessage.SET_RECORDING_STATUS_REQUEST:
|
||||
processSetRecordingStatusRequestMessage(message);
|
||||
break;
|
||||
break;
|
||||
case GetRecordingStatusRequestMessage.GET_RECORDING_STATUS_REQUEST:
|
||||
processGetRecordingStatusRequestMessage(message);
|
||||
break;
|
||||
@ -117,7 +93,21 @@ public class UsersMessageReceiver implements MessageHandler{
|
||||
case EjectUserFromVoiceRequestMessage.EJECT_USER_FROM_VOICE_REQUEST:
|
||||
processEjectUserFromVoiceRequestMessage(message);
|
||||
break;
|
||||
|
||||
case GetBreakoutRoomsList.NAME:
|
||||
bbbInGW.handleJsonMessage(message);
|
||||
break;
|
||||
case CreateBreakoutRoomsRequest.NAME:
|
||||
bbbInGW.handleJsonMessage(message);
|
||||
break;
|
||||
case ListenInOnBreakout.NAME:
|
||||
bbbInGW.handleJsonMessage(message);
|
||||
break;
|
||||
case RequestBreakoutJoinURL.NAME:
|
||||
bbbInGW.handleJsonMessage(message);
|
||||
break;
|
||||
case EndAllBreakoutRoomsRequest.NAME:
|
||||
bbbInGW.handleJsonMessage(message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -341,6 +331,6 @@ public class UsersMessageReceiver implements MessageHandler{
|
||||
EjectUserFromVoiceRequestMessage msg = EjectUserFromVoiceRequestMessage.fromJson(message);
|
||||
if (msg != null) {
|
||||
bbbInGW.ejectUserFromVoice(msg.meetingId, msg.userId, msg.requesterId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,28 @@
|
||||
/**
|
||||
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
|
||||
*
|
||||
* Copyright (c) 2012 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.recorders.events;
|
||||
|
||||
import org.bigbluebutton.core.service.recorder.RecordEvent;
|
||||
|
||||
public abstract class AbstractCaptionRecordEvent extends RecordEvent {
|
||||
|
||||
public AbstractCaptionRecordEvent() {
|
||||
setModule("CAPTION");
|
||||
}
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
/**
|
||||
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
|
||||
*
|
||||
* Copyright (c) 2012 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.recorders.events;
|
||||
|
||||
|
||||
public class EditCaptionHistoryRecordEvent extends AbstractCaptionRecordEvent {
|
||||
private static final String START_INDEX = "startIndex";
|
||||
private static final String END_INDEX = "endIndex";
|
||||
private static final String LOCALE = "locale";
|
||||
private static final String TEXT = "text";
|
||||
|
||||
public EditCaptionHistoryRecordEvent() {
|
||||
super();
|
||||
setEvent("EditCaptionHistoryEvent");
|
||||
}
|
||||
|
||||
public void setStartIndex(String startIndex) {
|
||||
eventMap.put(START_INDEX, startIndex);
|
||||
}
|
||||
|
||||
public void setEndIndex(String endIndex) {
|
||||
eventMap.put(END_INDEX, endIndex);
|
||||
}
|
||||
|
||||
public void setLocale(String locale) {
|
||||
eventMap.put(LOCALE, locale);
|
||||
}
|
||||
|
||||
public void setText(String text) {
|
||||
eventMap.put(TEXT, text);
|
||||
}
|
||||
}
|
96
akka-bbb-apps/src/main/java/org/bigbluebutton/core/util/WebApiUtil.java
Executable file
96
akka-bbb-apps/src/main/java/org/bigbluebutton/core/util/WebApiUtil.java
Executable file
@ -0,0 +1,96 @@
|
||||
package org.bigbluebutton.core.util;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.Map;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
public class WebApiUtil {
|
||||
|
||||
private final String securitySalt = "changeme";
|
||||
|
||||
//
|
||||
//checksum() -- Return a checksum based on SHA-1 digest
|
||||
//
|
||||
public String checksum(String s) {
|
||||
String checksum = "";
|
||||
try {
|
||||
checksum = DigestUtils.shaHex(s);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return checksum;
|
||||
}
|
||||
|
||||
//From the list of parameters we want to pass. Creates a base string with parameters
|
||||
//sorted in alphabetical order for us to sign.
|
||||
public String createBaseString(Map<String, String[]> params) {
|
||||
StringBuffer csbuf = new StringBuffer();
|
||||
SortedSet<String> keys = new TreeSet<String>(params.keySet());
|
||||
|
||||
boolean first = true;
|
||||
for (String key: keys) {
|
||||
for (String value: params.get(key)) {
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
csbuf.append("&");
|
||||
}
|
||||
csbuf.append(key);
|
||||
csbuf.append("=");
|
||||
csbuf.append(value);
|
||||
}
|
||||
}
|
||||
|
||||
return csbuf.toString();
|
||||
}
|
||||
|
||||
public boolean isChecksumSame(String apiCall, String checksum, String queryString) {
|
||||
if (StringUtils.isEmpty(securitySalt)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if( queryString == null ) {
|
||||
queryString = "";
|
||||
} else {
|
||||
// handle either checksum as first or middle / end parameter
|
||||
// TODO: this is hackish - should be done better
|
||||
queryString = queryString.replace("&checksum=" + checksum, "");
|
||||
queryString = queryString.replace("checksum=" + checksum + "&", "");
|
||||
queryString = queryString.replace("checksum=" + checksum, "");
|
||||
}
|
||||
|
||||
String cs = DigestUtils.shaHex(apiCall + queryString + securitySalt);
|
||||
|
||||
if (cs == null || cs.equals(checksum) == false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
//encodeURIComponent() -- Java encoding similiar to JavaScript encodeURIComponent
|
||||
//
|
||||
public String encodeURIComponent(String component) {
|
||||
String result = null;
|
||||
|
||||
try {
|
||||
result = URLEncoder.encode(component, "UTF-8")
|
||||
.replaceAll("\\%28", "(")
|
||||
.replaceAll("\\%29", ")")
|
||||
.replaceAll("\\+", "%20")
|
||||
.replaceAll("\\%27", "'")
|
||||
.replaceAll("\\%21", "!")
|
||||
.replaceAll("\\%7E", "~");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
result = component;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
@ -33,8 +33,23 @@ redis {
|
||||
password=""
|
||||
# recording keys should expire in 14 days
|
||||
keyExpiry=1209600
|
||||
}
|
||||
|
||||
http {
|
||||
interface = "0.0.0.0"
|
||||
port = 9000
|
||||
}
|
||||
|
||||
services {
|
||||
bbbWebHost = "localhost"
|
||||
bbbWebPort = 88888
|
||||
bbbWebAPI = "http://192.168.23.33/bigbluebutton/api"
|
||||
sharedSecret = "changeme"
|
||||
moderatorPassword = "mp"
|
||||
viewerPassword = "ap"
|
||||
defaultPresentationURL = "http://localhost/default.pdf"
|
||||
}
|
||||
|
||||
red5 {
|
||||
deskshareip="192.168.0.109"
|
||||
deskshareapp="video-broadcast"
|
||||
|
@ -1,10 +1,10 @@
|
||||
package org.bigbluebutton
|
||||
|
||||
import akka.event.{ LoggingAdapter, Logging }
|
||||
import akka.actor.{ ActorSystem, Props }
|
||||
import scala.concurrent.duration._
|
||||
import redis.RedisClient
|
||||
import scala.concurrent.{ Future, Await }
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
import org.bigbluebutton.endpoint.redis.RedisPublisher
|
||||
import org.bigbluebutton.endpoint.redis.KeepAliveRedisPublisher
|
||||
import org.bigbluebutton.endpoint.redis.AppsRedisSubscriberActor
|
||||
@ -12,15 +12,27 @@ import org.bigbluebutton.core.api.MessageOutGateway
|
||||
import org.bigbluebutton.core.api.IBigBlueButtonInGW
|
||||
import org.bigbluebutton.core.BigBlueButtonInGW
|
||||
import org.bigbluebutton.core.MessageSender
|
||||
import org.bigbluebutton.core.OutMessageGateway
|
||||
import org.bigbluebutton.core.MessageSenderActor
|
||||
import org.bigbluebutton.core.RecorderActor
|
||||
import org.bigbluebutton.core.pubsub.receivers.RedisMessageReceiver
|
||||
import org.bigbluebutton.core.api.OutMessageListener2
|
||||
import org.bigbluebutton.core.pubsub.senders._
|
||||
import org.bigbluebutton.core.service.recorder.RedisDispatcher
|
||||
import org.bigbluebutton.core.service.recorder.RecorderApplication
|
||||
import org.bigbluebutton.core.bus._
|
||||
import org.bigbluebutton.core.JsonMessageSenderActor
|
||||
|
||||
object Boot extends App with SystemConfiguration {
|
||||
|
||||
implicit val system = ActorSystem("bigbluebutton-apps-system")
|
||||
implicit val executor = system.dispatcher
|
||||
val logger = Logging(system, getClass)
|
||||
|
||||
val eventBus = new IncomingEventBus
|
||||
val outgoingEventBus = new OutgoingEventBus
|
||||
|
||||
val outGW = new OutMessageGateway(outgoingEventBus)
|
||||
|
||||
val redisPublisher = new RedisPublisher(system)
|
||||
val msgSender = new MessageSender(redisPublisher)
|
||||
@ -29,7 +41,15 @@ object Boot extends App with SystemConfiguration {
|
||||
val recorderApp = new RecorderApplication(redisDispatcher)
|
||||
recorderApp.start()
|
||||
|
||||
val bbbInGW = new BigBlueButtonInGW(system, recorderApp, msgSender, red5DeskShareIP, red5DeskShareApp)
|
||||
val messageSenderActor = system.actorOf(MessageSenderActor.props(msgSender), "messageSenderActor")
|
||||
val recorderActor = system.actorOf(RecorderActor.props(recorderApp), "recorderActor")
|
||||
val newMessageSenderActor = system.actorOf(JsonMessageSenderActor.props(msgSender), "newMessageSenderActor")
|
||||
|
||||
outgoingEventBus.subscribe(messageSenderActor, "outgoingMessageChannel")
|
||||
outgoingEventBus.subscribe(recorderActor, "outgoingMessageChannel")
|
||||
outgoingEventBus.subscribe(newMessageSenderActor, "outgoingMessageChannel")
|
||||
|
||||
val bbbInGW = new BigBlueButtonInGW(system, eventBus, outGW, red5DeskShareIP, red5DeskShareApp)
|
||||
val redisMsgReceiver = new RedisMessageReceiver(bbbInGW)
|
||||
|
||||
val redisSubscriberActor = system.actorOf(AppsRedisSubscriberActor.props(redisMsgReceiver), "redis-subscriber")
|
||||
|
@ -10,7 +10,16 @@ trait SystemConfiguration {
|
||||
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 redisPassword = Try(config.getString("redis.password")).getOrElse("")
|
||||
lazy val httpInterface = Try(config.getString("http.interface")).getOrElse("")
|
||||
lazy val httpPort = Try(config.getInt("http.port")).getOrElse(9090)
|
||||
lazy val bbbWebHost = Try(config.getString("services.bbbWebHost")).getOrElse("localhost")
|
||||
lazy val bbbWebPort = Try(config.getInt("services.bbbWebPort")).getOrElse(8888)
|
||||
lazy val bbbWebAPI = Try(config.getString("services.bbbWebAPI")).getOrElse("localhost")
|
||||
lazy val bbbWebSharedSecret = Try(config.getString("services.sharedSecret")).getOrElse("changeme")
|
||||
lazy val bbbWebModeratorPassword = Try(config.getString("services.moderatorPassword")).getOrElse("changeme")
|
||||
lazy val bbbWebViewerPassword = Try(config.getString("services.viewerPassword")).getOrElse("changeme")
|
||||
lazy val bbbWebDefaultPresentationURL = Try(config.getString("services.defaultPresentationURL")).getOrElse("changeme")
|
||||
lazy val keysExpiresInSec = Try(config.getInt("redis.keyExpiry")).getOrElse(14 * 86400) // 14 days
|
||||
lazy val red5DeskShareIP = Try(config.getString("red5.deskshareip")).getOrElse("127.0.0.1")
|
||||
lazy val red5DeskShareApp = Try(config.getString("red5.deskshareapp")).getOrElse("")
|
||||
}
|
||||
}
|
||||
|
@ -5,34 +5,24 @@ import akka.actor.ActorLogging
|
||||
import akka.pattern.{ ask, pipe }
|
||||
import akka.util.Timeout
|
||||
import scala.concurrent.duration._
|
||||
import scala.collection.mutable.HashMap
|
||||
import org.bigbluebutton.core.bus._
|
||||
import org.bigbluebutton.core.api._
|
||||
import org.bigbluebutton.core.util._
|
||||
import org.bigbluebutton.core.api.ValidateAuthTokenTimedOut
|
||||
import scala.util.Success
|
||||
import scala.util.Failure
|
||||
import org.bigbluebutton.SystemConfiguration
|
||||
import org.bigbluebutton.core.recorders.events.VoiceUserJoinedRecordEvent
|
||||
import org.bigbluebutton.core.recorders.events.VoiceUserLeftRecordEvent
|
||||
import org.bigbluebutton.core.recorders.events.VoiceUserLockedRecordEvent
|
||||
import org.bigbluebutton.core.recorders.events.VoiceUserMutedRecordEvent
|
||||
import org.bigbluebutton.core.recorders.events.VoiceStartRecordingRecordEvent
|
||||
import org.bigbluebutton.core.recorders.events.VoiceUserTalkingRecordEvent
|
||||
import org.bigbluebutton.core.service.recorder.RecorderApplication
|
||||
import scala.collection._
|
||||
import com.google.gson.Gson
|
||||
|
||||
object BigBlueButtonActor extends SystemConfiguration {
|
||||
def props(system: ActorSystem, recorderApp: RecorderApplication, messageSender: MessageSender): Props =
|
||||
Props(classOf[BigBlueButtonActor], system, recorderApp, messageSender)
|
||||
def props(system: ActorSystem,
|
||||
eventBus: IncomingEventBus,
|
||||
outGW: OutMessageGateway): Props =
|
||||
Props(classOf[BigBlueButtonActor], system, eventBus, outGW)
|
||||
}
|
||||
|
||||
class BigBlueButtonActor(val system: ActorSystem, recorderApp: RecorderApplication, messageSender: MessageSender) extends Actor with ActorLogging {
|
||||
class BigBlueButtonActor(val system: ActorSystem,
|
||||
eventBus: IncomingEventBus, outGW: OutMessageGateway) extends Actor with ActorLogging {
|
||||
|
||||
implicit def executionContext = system.dispatcher
|
||||
implicit val timeout = Timeout(5 seconds)
|
||||
|
||||
private var meetings = new collection.immutable.HashMap[String, RunningMeeting]
|
||||
private val outGW = new OutMessageGateway("bbbActorOutGW", recorderApp, messageSender)
|
||||
|
||||
def receive = {
|
||||
case msg: CreateMeeting => handleCreateMeeting(msg)
|
||||
@ -52,21 +42,15 @@ class BigBlueButtonActor(val system: ActorSystem, recorderApp: RecorderApplicati
|
||||
case msg: DeskShareRTMPBroadcastStartedRequest => handleDeskShareRTMPBroadcastStartedRequest(msg)
|
||||
case msg: DeskShareRTMPBroadcastStoppedRequest => handleDeskShareRTMPBroadcastStoppedRequest(msg)
|
||||
case msg: DeskShareGetDeskShareInfoRequest => handleDeskShareGetDeskShareInfoRequest(msg)
|
||||
case msg: InMessage => handleMeetingMessage(msg)
|
||||
case _ => // do nothing
|
||||
}
|
||||
|
||||
private def findMeetingWithVoiceConfId(voiceConfId: String): Option[RunningMeeting] = {
|
||||
meetings.values.find(m => {
|
||||
println("+++ compare " + m.mProps.voiceBridge + " with our " + voiceConfId)
|
||||
m.mProps.voiceBridge == voiceConfId
|
||||
})
|
||||
meetings.values.find(m => { m.mProps.voiceBridge == voiceConfId })
|
||||
}
|
||||
|
||||
private def handleUserJoinedVoiceConfMessage(msg: UserJoinedVoiceConfMessage) {
|
||||
findMeetingWithVoiceConfId(msg.voiceConfId) foreach { m =>
|
||||
m.actorRef ! msg
|
||||
}
|
||||
findMeetingWithVoiceConfId(msg.voiceConfId) foreach { m => m.actorRef ! msg }
|
||||
}
|
||||
|
||||
private def handleUserLeftVoiceConfMessage(msg: UserLeftVoiceConfMessage) {
|
||||
@ -98,62 +82,27 @@ class BigBlueButtonActor(val system: ActorSystem, recorderApp: RecorderApplicati
|
||||
findMeetingWithVoiceConfId(msg.voiceConfId) foreach { m =>
|
||||
m.actorRef ! msg
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private def handleValidateAuthToken(msg: ValidateAuthToken) {
|
||||
meetings.get(msg.meetingID) foreach { m =>
|
||||
val future = m.actorRef.ask(msg)(5 seconds)
|
||||
m.actorRef ! msg
|
||||
|
||||
future onComplete {
|
||||
case Success(result) => {
|
||||
log.info("Validate auth token response. meetingId=" + msg.meetingID + " userId=" + msg.userId + " token=" + msg.token)
|
||||
/**
|
||||
* Received a reply from MeetingActor which means hasn't hung!
|
||||
* Sometimes, the actor seems to hang and doesn't anymore accept messages. This is a simple
|
||||
* audit to check whether the actor is still alive. (ralam feb 25, 2015)
|
||||
*/
|
||||
}
|
||||
case Failure(failure) => {
|
||||
log.warning("Validate auth token timeout. meetingId=" + msg.meetingID + " userId=" + msg.userId + " token=" + msg.token)
|
||||
outGW.send(new ValidateAuthTokenTimedOut(msg.meetingID, msg.userId, msg.token, false, msg.correlationId))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private def handleMeetingMessage(msg: InMessage): Unit = {
|
||||
msg match {
|
||||
case ucm: UserConnectedToGlobalAudio => {
|
||||
val m = meetings.values.find(m => m.mProps.voiceBridge == ucm.voiceConf)
|
||||
m foreach { mActor => mActor.actorRef ! ucm }
|
||||
}
|
||||
case udm: UserDisconnectedFromGlobalAudio => {
|
||||
val m = meetings.values.find(m => m.mProps.voiceBridge == udm.voiceConf)
|
||||
m foreach { mActor => mActor.actorRef ! udm }
|
||||
}
|
||||
case allOthers => {
|
||||
meetings.get(allOthers.meetingID) match {
|
||||
case None => handleMeetingNotFound(allOthers)
|
||||
case Some(m) => {
|
||||
// log.debug("Forwarding message [{}] to meeting [{}]", msg.meetingID)
|
||||
m.actorRef ! allOthers
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private def handleMeetingNotFound(msg: InMessage) {
|
||||
msg match {
|
||||
case vat: ValidateAuthToken => {
|
||||
log.info("No meeting [" + vat.meetingID + "] for auth token [" + vat.token + "]")
|
||||
outGW.send(new ValidateAuthTokenReply(vat.meetingID, vat.userId, vat.token, false, vat.correlationId))
|
||||
}
|
||||
case _ => {
|
||||
log.info("No meeting [" + msg.meetingID + "] for message type [" + msg.getClass() + "]")
|
||||
// do nothing
|
||||
}
|
||||
// val future = m.actorRef.ask(msg)(5 seconds)
|
||||
// future onComplete {
|
||||
// case Success(result) => {
|
||||
// log.info("Validate auth token response. meetingId=" + msg.meetingID + " userId=" + msg.userId + " token=" + msg.token)
|
||||
// /**
|
||||
// * Received a reply from MeetingActor which means hasn't hung!
|
||||
// * Sometimes, the actor seems to hang and doesn't anymore accept messages. This is a simple
|
||||
// * audit to check whether the actor is still alive. (ralam feb 25, 2015)
|
||||
// */
|
||||
// }
|
||||
// case Failure(failure) => {
|
||||
// log.warning("Validate auth token timeout. meetingId=" + msg.meetingID + " userId=" + msg.userId + " token=" + msg.token)
|
||||
// outGW.send(new ValidateAuthTokenTimedOut(msg.meetingID, msg.userId, msg.token, false, msg.correlationId))
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
@ -162,7 +111,6 @@ class BigBlueButtonActor(val system: ActorSystem, recorderApp: RecorderApplicati
|
||||
}
|
||||
|
||||
private def handlePubSubPingMessage(msg: PubSubPing): Unit = {
|
||||
//log.info("PubSubPing from [" + msg.system + "]")
|
||||
outGW.send(new PubSubPong(msg.system, msg.timestamp))
|
||||
}
|
||||
|
||||
@ -173,11 +121,20 @@ class BigBlueButtonActor(val system: ActorSystem, recorderApp: RecorderApplicati
|
||||
case Some(m) => {
|
||||
meetings -= msg.meetingID
|
||||
log.info("Kick everyone out on meetingId={}", msg.meetingID)
|
||||
if (m.mProps.isBreakout) {
|
||||
log.info("Informing parent meeting {} that a breakout room has been ended{}", m.mProps.externalMeetingID, m.mProps.meetingID)
|
||||
eventBus.publish(BigBlueButtonEvent(m.mProps.externalMeetingID,
|
||||
BreakoutRoomEnded(m.mProps.externalMeetingID, m.mProps.meetingID)))
|
||||
}
|
||||
outGW.send(new EndAndKickAll(msg.meetingID, m.mProps.recorded))
|
||||
outGW.send(new DisconnectAllUsers(msg.meetingID))
|
||||
log.info("Destroyed meetingId={}", msg.meetingID)
|
||||
outGW.send(new MeetingDestroyed(msg.meetingID))
|
||||
|
||||
/** Unsubscribe to meeting and voice events. **/
|
||||
eventBus.unsubscribe(m.actorRef, m.mProps.meetingID)
|
||||
eventBus.unsubscribe(m.actorRef, m.mProps.voiceBridge)
|
||||
|
||||
// Stop the meeting actor.
|
||||
context.stop(m.actorRef)
|
||||
}
|
||||
@ -188,8 +145,12 @@ class BigBlueButtonActor(val system: ActorSystem, recorderApp: RecorderApplicati
|
||||
meetings.get(msg.meetingID) match {
|
||||
case None => {
|
||||
log.info("Create meeting request. meetingId={}", msg.mProps.meetingID)
|
||||
val moutGW = new OutMessageGateway("meetingOutGW-" + msg.meetingID, recorderApp, messageSender)
|
||||
var m = RunningMeeting(msg.mProps, moutGW)
|
||||
|
||||
var m = RunningMeeting(msg.mProps, outGW, eventBus)
|
||||
|
||||
/** Subscribe to meeting and voice events. **/
|
||||
eventBus.subscribe(m.actorRef, m.mProps.meetingID)
|
||||
eventBus.subscribe(m.actorRef, m.mProps.voiceBridge)
|
||||
|
||||
meetings += m.mProps.meetingID -> m
|
||||
outGW.send(new MeetingCreated(m.mProps.meetingID, m.mProps.externalMeetingID, m.mProps.recorded, m.mProps.meetingName,
|
||||
@ -197,7 +158,6 @@ class BigBlueButtonActor(val system: ActorSystem, recorderApp: RecorderApplicati
|
||||
msg.mProps.viewerPass, msg.mProps.createTime, msg.mProps.createDate))
|
||||
|
||||
m.actorRef ! new InitializeMeeting(m.mProps.meetingID, m.mProps.recorded)
|
||||
m.actorRef ! "StartTimer"
|
||||
}
|
||||
case Some(m) => {
|
||||
log.info("Meeting already created. meetingID={}", msg.mProps.meetingID)
|
||||
@ -207,42 +167,41 @@ class BigBlueButtonActor(val system: ActorSystem, recorderApp: RecorderApplicati
|
||||
}
|
||||
|
||||
private def handleGetAllMeetingsRequest(msg: GetAllMeetingsRequest) {
|
||||
val len = meetings.keys.size
|
||||
var currPosition = len - 1
|
||||
var resultArray: Array[MeetingInfo] = new Array[MeetingInfo](len)
|
||||
|
||||
var len = meetings.keys.size
|
||||
println("meetings.size=" + meetings.size)
|
||||
println("len_=" + len)
|
||||
meetings.values.foreach(m => {
|
||||
val id = m.mProps.meetingID
|
||||
val duration = m.mProps.duration
|
||||
val name = m.mProps.meetingName
|
||||
val recorded = m.mProps.recorded
|
||||
val voiceBridge = m.mProps.voiceBridge
|
||||
|
||||
val set = meetings.keySet
|
||||
val arr: Array[String] = new Array[String](len)
|
||||
set.copyToArray(arr)
|
||||
val resultArray: Array[MeetingInfo] = new Array[MeetingInfo](len)
|
||||
val info = new MeetingInfo(id, name, recorded, voiceBridge, duration)
|
||||
resultArray(currPosition) = info
|
||||
currPosition = currPosition - 1
|
||||
|
||||
for (i <- 0 until arr.length) {
|
||||
val id = arr(i)
|
||||
val duration = meetings.get(arr(i)).head.mProps.duration
|
||||
val name = meetings.get(arr(i)).head.mProps.meetingName
|
||||
val recorded = meetings.get(arr(i)).head.mProps.recorded
|
||||
val voiceBridge = meetings.get(arr(i)).head.mProps.voiceBridge
|
||||
|
||||
var info = new MeetingInfo(id, name, recorded, voiceBridge, duration)
|
||||
resultArray(i) = info
|
||||
val html5clientRequesterID = "nodeJSapp"
|
||||
|
||||
//send the users
|
||||
self ! (new GetUsers(id, "nodeJSapp"))
|
||||
eventBus.publish(BigBlueButtonEvent(id, new GetUsers(id, html5clientRequesterID)))
|
||||
|
||||
//send the presentation
|
||||
self ! (new GetPresentationInfo(id, "nodeJSapp", "nodeJSapp"))
|
||||
eventBus.publish(BigBlueButtonEvent(id, new GetPresentationInfo(id, html5clientRequesterID, html5clientRequesterID)))
|
||||
|
||||
//send chat history
|
||||
self ! (new GetChatHistoryRequest(id, "nodeJSapp", "nodeJSapp"))
|
||||
eventBus.publish(BigBlueButtonEvent(id, new GetChatHistoryRequest(id, html5clientRequesterID, html5clientRequesterID)))
|
||||
|
||||
//send lock settings
|
||||
self ! (new GetLockSettings(id, "nodeJSapp"))
|
||||
eventBus.publish(BigBlueButtonEvent(id, new GetLockSettings(id, html5clientRequesterID)))
|
||||
|
||||
//send desktop sharing info
|
||||
self ! (new DeskShareGetDeskShareInfoRequest(id, "nodeJSapp", "nodeJSapp"))
|
||||
eventBus.publish(BigBlueButtonEvent(id, new DeskShareGetDeskShareInfoRequest(id, html5clientRequesterID, html5clientRequesterID)))
|
||||
|
||||
}
|
||||
// send captions
|
||||
eventBus.publish(BigBlueButtonEvent(id, new SendCaptionHistoryRequest(id, html5clientRequesterID)))
|
||||
})
|
||||
|
||||
outGW.send(new GetAllMeetingsReply(resultArray))
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package org.bigbluebutton.core
|
||||
|
||||
import org.bigbluebutton.core.bus._
|
||||
import org.bigbluebutton.core.api._
|
||||
import scala.collection.JavaConversions._
|
||||
import java.util.ArrayList
|
||||
@ -17,59 +18,108 @@ import org.bigbluebutton.core.service.recorder.RecorderApplication
|
||||
import org.bigbluebutton.common.messages.IBigBlueButtonMessage
|
||||
import org.bigbluebutton.common.messages.StartCustomPollRequestMessage
|
||||
import org.bigbluebutton.common.messages.PubSubPingMessage
|
||||
import org.bigbluebutton.messages._
|
||||
import org.bigbluebutton.messages.payload._
|
||||
import akka.event.Logging
|
||||
import spray.json.JsonParser
|
||||
|
||||
class BigBlueButtonInGW(val system: ActorSystem, recorderApp: RecorderApplication, messageSender: MessageSender,
|
||||
val red5DeskShareIP: String, val red5DeskShareApp: String) extends IBigBlueButtonInGW {
|
||||
class BigBlueButtonInGW(
|
||||
val system: ActorSystem,
|
||||
eventBus: IncomingEventBus,
|
||||
outGW: OutMessageGateway,
|
||||
val red5DeskShareIP: String,
|
||||
val red5DeskShareApp: String) extends IBigBlueButtonInGW {
|
||||
|
||||
val log = system.log
|
||||
val bbbActor = system.actorOf(BigBlueButtonActor.props(system, recorderApp, messageSender), "bigbluebutton-actor")
|
||||
val log = Logging(system, getClass)
|
||||
val bbbActor = system.actorOf(BigBlueButtonActor.props(system, eventBus, outGW), "bigbluebutton-actor")
|
||||
eventBus.subscribe(bbbActor, "meeting-manager")
|
||||
|
||||
def handleBigBlueButtonMessage(message: IBigBlueButtonMessage) {
|
||||
message match {
|
||||
case msg: StartCustomPollRequestMessage => {
|
||||
bbbActor ! new StartCustomPollRequest(msg.payload.meetingId, msg.payload.requesterId, msg.payload.pollType, msg.payload.answers)
|
||||
eventBus.publish(
|
||||
BigBlueButtonEvent(
|
||||
"meeting-manager",
|
||||
new StartCustomPollRequest(
|
||||
msg.payload.meetingId,
|
||||
msg.payload.requesterId,
|
||||
msg.payload.pollType,
|
||||
msg.payload.answers)))
|
||||
}
|
||||
case msg: PubSubPingMessage => {
|
||||
bbbActor ! new PubSubPing(msg.payload.system, msg.payload.timestamp)
|
||||
eventBus.publish(
|
||||
BigBlueButtonEvent(
|
||||
"meeting-manager",
|
||||
new PubSubPing(msg.payload.system, msg.payload.timestamp)))
|
||||
}
|
||||
|
||||
case msg: CreateMeetingRequest => {
|
||||
val mProps = new MeetingProperties(
|
||||
msg.payload.id,
|
||||
msg.payload.externalId,
|
||||
msg.payload.name,
|
||||
msg.payload.record,
|
||||
msg.payload.voiceConfId,
|
||||
msg.payload.durationInMinutes,
|
||||
msg.payload.autoStartRecording,
|
||||
msg.payload.allowStartStopRecording,
|
||||
msg.payload.moderatorPassword,
|
||||
msg.payload.viewerPassword,
|
||||
msg.payload.createTime,
|
||||
msg.payload.createDate,
|
||||
red5DeskShareIP, red5DeskShareApp,
|
||||
msg.payload.isBreakout)
|
||||
|
||||
eventBus.publish(BigBlueButtonEvent("meeting-manager", new CreateMeeting(msg.payload.id, mProps)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Meeting
|
||||
def createMeeting2(meetingID: String, externalMeetingID: String, meetingName: String, record: Boolean,
|
||||
voiceBridge: String, duration: Long, autoStartRecording: Boolean,
|
||||
allowStartStopRecording: Boolean, moderatorPass: String, viewerPass: String,
|
||||
createTime: Long, createDate: String) {
|
||||
def handleJsonMessage(json: String) {
|
||||
JsonMessageDecoder.decode(json) match {
|
||||
case Some(validMsg) => forwardMessage(validMsg)
|
||||
case None => log.error("Unhandled json message: {}", json)
|
||||
}
|
||||
}
|
||||
|
||||
val mProps = new MeetingProperties(meetingID, externalMeetingID, meetingName, record,
|
||||
voiceBridge, duration, autoStartRecording, allowStartStopRecording,
|
||||
moderatorPass, viewerPass, createTime, createDate, red5DeskShareIP, red5DeskShareApp)
|
||||
bbbActor ! new CreateMeeting(meetingID, mProps)
|
||||
def forwardMessage(msg: InMessage) = {
|
||||
msg match {
|
||||
case m: BreakoutRoomsListMessage => eventBus.publish(BigBlueButtonEvent(m.meetingId, m))
|
||||
case m: CreateBreakoutRooms => eventBus.publish(BigBlueButtonEvent(m.meetingId, m))
|
||||
case m: RequestBreakoutJoinURLInMessage => eventBus.publish(BigBlueButtonEvent(m.meetingId, m))
|
||||
case m: TransferUserToMeetingRequest => eventBus.publish(BigBlueButtonEvent(m.meetingId, m))
|
||||
case m: EndAllBreakoutRooms => eventBus.publish(BigBlueButtonEvent(m.meetingId, m))
|
||||
case _ => log.error("Unhandled message: {}", msg)
|
||||
}
|
||||
}
|
||||
|
||||
def destroyMeeting(meetingID: String) {
|
||||
bbbActor ! new DestroyMeeting(meetingID)
|
||||
forwardMessage(new EndAllBreakoutRooms(meetingID))
|
||||
eventBus.publish(
|
||||
BigBlueButtonEvent(
|
||||
"meeting-manager",
|
||||
new DestroyMeeting(
|
||||
meetingID)))
|
||||
}
|
||||
|
||||
def getAllMeetings(meetingID: String) {
|
||||
bbbActor ! new GetAllMeetingsRequest("meetingId")
|
||||
eventBus.publish(BigBlueButtonEvent("meeting-manager", new GetAllMeetingsRequest("meetingId")))
|
||||
}
|
||||
|
||||
def isAliveAudit(aliveId: String) {
|
||||
bbbActor ! new KeepAliveMessage(aliveId)
|
||||
eventBus.publish(BigBlueButtonEvent("meeting-manager", new KeepAliveMessage(aliveId)))
|
||||
}
|
||||
|
||||
def lockSettings(meetingID: String, locked: java.lang.Boolean,
|
||||
lockSettings: java.util.Map[String, java.lang.Boolean]) {
|
||||
|
||||
}
|
||||
|
||||
def statusMeetingAudit(meetingID: String) {
|
||||
|
||||
}
|
||||
|
||||
def endMeeting(meetingID: String) {
|
||||
bbbActor ! new EndMeeting(meetingID)
|
||||
def endMeeting(meetingId: String) {
|
||||
eventBus.publish(BigBlueButtonEvent(meetingId, new EndMeeting(meetingId)))
|
||||
}
|
||||
|
||||
def endAllMeetings() {
|
||||
@ -82,12 +132,12 @@ class BigBlueButtonInGW(val system: ActorSystem, recorderApp: RecorderApplicatio
|
||||
* ***********************************************************
|
||||
*/
|
||||
def validateAuthToken(meetingId: String, userId: String, token: String, correlationId: String, sessionId: String) {
|
||||
bbbActor ! new ValidateAuthToken(meetingId, userId, token, correlationId, sessionId)
|
||||
eventBus.publish(BigBlueButtonEvent(meetingId, new ValidateAuthToken(meetingId, userId, token, correlationId, sessionId)))
|
||||
}
|
||||
|
||||
def registerUser(meetingID: String, userID: String, name: String, role: String, extUserID: String, authToken: String, avatarURL: String): Unit = {
|
||||
val userRole = if (role == "MODERATOR") Role.MODERATOR else Role.VIEWER
|
||||
bbbActor ! new RegisterUser(meetingID, userID, name, userRole, extUserID, authToken, avatarURL)
|
||||
eventBus.publish(BigBlueButtonEvent(meetingID, new RegisterUser(meetingID, userID, name, userRole, extUserID, authToken, avatarURL)))
|
||||
}
|
||||
|
||||
def sendLockSettings(meetingID: String, userId: String, settings: java.util.Map[String, java.lang.Boolean]) {
|
||||
@ -112,7 +162,7 @@ class BigBlueButtonInGW(val system: ActorSystem, recorderApp: RecorderApplicatio
|
||||
lockOnJoin = lockOnJoin,
|
||||
lockOnJoinConfigurable = lockOnJoinConfigurable)
|
||||
|
||||
bbbActor ! new SetLockSettings(meetingID, userId, permissions)
|
||||
eventBus.publish(BigBlueButtonEvent(meetingID, new SetLockSettings(meetingID, userId, permissions)))
|
||||
}
|
||||
|
||||
def initLockSettings(meetingID: String, settings: java.util.Map[String, java.lang.Boolean]) {
|
||||
@ -136,64 +186,64 @@ class BigBlueButtonInGW(val system: ActorSystem, recorderApp: RecorderApplicatio
|
||||
lockOnJoin = lockOnJoin,
|
||||
lockOnJoinConfigurable = lockOnJoinConfigurable)
|
||||
|
||||
bbbActor ! new InitLockSettings(meetingID, permissions)
|
||||
eventBus.publish(BigBlueButtonEvent(meetingID, new InitLockSettings(meetingID, permissions)))
|
||||
}
|
||||
|
||||
def initAudioSettings(meetingID: String, requesterID: String, muted: java.lang.Boolean) {
|
||||
bbbActor ! new InitAudioSettings(meetingID, requesterID, muted.booleanValue())
|
||||
eventBus.publish(BigBlueButtonEvent(meetingID, new InitAudioSettings(meetingID, requesterID, muted.booleanValue())))
|
||||
}
|
||||
|
||||
def getLockSettings(meetingId: String, userId: String) {
|
||||
bbbActor ! new GetLockSettings(meetingId, userId)
|
||||
eventBus.publish(BigBlueButtonEvent(meetingId, new GetLockSettings(meetingId, userId)))
|
||||
}
|
||||
|
||||
def lockUser(meetingId: String, requesterID: String, lock: Boolean, userId: String) {
|
||||
bbbActor ! new LockUserRequest(meetingId, requesterID, userId, lock)
|
||||
eventBus.publish(BigBlueButtonEvent(meetingId, new LockUserRequest(meetingId, requesterID, userId, lock)))
|
||||
}
|
||||
|
||||
def setRecordingStatus(meetingId: String, userId: String, recording: java.lang.Boolean) {
|
||||
bbbActor ! new SetRecordingStatus(meetingId, userId, recording.booleanValue())
|
||||
eventBus.publish(BigBlueButtonEvent(meetingId, new SetRecordingStatus(meetingId, userId, recording.booleanValue())))
|
||||
}
|
||||
|
||||
def getRecordingStatus(meetingId: String, userId: String) {
|
||||
bbbActor ! new GetRecordingStatus(meetingId, userId)
|
||||
eventBus.publish(BigBlueButtonEvent(meetingId, new GetRecordingStatus(meetingId, userId)))
|
||||
}
|
||||
|
||||
// Users
|
||||
def userEmojiStatus(meetingId: String, userId: String, emojiStatus: String) {
|
||||
bbbActor ! new UserEmojiStatus(meetingId, userId, emojiStatus)
|
||||
eventBus.publish(BigBlueButtonEvent(meetingId, new UserEmojiStatus(meetingId, userId, emojiStatus)))
|
||||
}
|
||||
|
||||
def ejectUserFromMeeting(meetingId: String, userId: String, ejectedBy: String) {
|
||||
bbbActor ! new EjectUserFromMeeting(meetingId, userId, ejectedBy)
|
||||
eventBus.publish(BigBlueButtonEvent(meetingId, new EjectUserFromMeeting(meetingId, userId, ejectedBy)))
|
||||
}
|
||||
|
||||
def shareWebcam(meetingId: String, userId: String, stream: String) {
|
||||
bbbActor ! new UserShareWebcam(meetingId, userId, stream)
|
||||
eventBus.publish(BigBlueButtonEvent(meetingId, new UserShareWebcam(meetingId, userId, stream)))
|
||||
}
|
||||
|
||||
def unshareWebcam(meetingId: String, userId: String, stream: String) {
|
||||
bbbActor ! new UserUnshareWebcam(meetingId, userId, stream)
|
||||
eventBus.publish(BigBlueButtonEvent(meetingId, new UserUnshareWebcam(meetingId, userId, stream)))
|
||||
}
|
||||
|
||||
def setUserStatus(meetingID: String, userID: String, status: String, value: Object) {
|
||||
bbbActor ! new ChangeUserStatus(meetingID, userID, status, value)
|
||||
eventBus.publish(BigBlueButtonEvent(meetingID, new ChangeUserStatus(meetingID, userID, status, value)))
|
||||
}
|
||||
|
||||
def getUsers(meetingID: String, requesterID: String) {
|
||||
bbbActor ! new GetUsers(meetingID, requesterID)
|
||||
eventBus.publish(BigBlueButtonEvent(meetingID, new GetUsers(meetingID, requesterID)))
|
||||
}
|
||||
|
||||
def userLeft(meetingID: String, userID: String, sessionId: String): Unit = {
|
||||
bbbActor ! new UserLeaving(meetingID, userID, sessionId)
|
||||
eventBus.publish(BigBlueButtonEvent(meetingID, new UserLeaving(meetingID, userID, sessionId)))
|
||||
}
|
||||
|
||||
def userJoin(meetingID: String, userID: String, authToken: String): Unit = {
|
||||
bbbActor ! new UserJoining(meetingID, userID, authToken)
|
||||
eventBus.publish(BigBlueButtonEvent(meetingID, new UserJoining(meetingID, userID, authToken)))
|
||||
}
|
||||
|
||||
def assignPresenter(meetingID: String, newPresenterID: String, newPresenterName: String, assignedBy: String): Unit = {
|
||||
bbbActor ! new AssignPresenter(meetingID, newPresenterID, newPresenterName, assignedBy)
|
||||
eventBus.publish(BigBlueButtonEvent(meetingID, new AssignPresenter(meetingID, newPresenterID, newPresenterName, assignedBy)))
|
||||
}
|
||||
|
||||
def getCurrentPresenter(meetingID: String, requesterID: String): Unit = {
|
||||
@ -203,13 +253,13 @@ class BigBlueButtonInGW(val system: ActorSystem, recorderApp: RecorderApplicatio
|
||||
def userConnectedToGlobalAudio(voiceConf: String, userid: String, name: String) {
|
||||
// we are required to pass the meeting_id as first parameter (just to satisfy trait)
|
||||
// but it's not used anywhere. That's why we pass voiceConf twice instead
|
||||
bbbActor ! new UserConnectedToGlobalAudio(voiceConf, voiceConf, userid, name)
|
||||
eventBus.publish(BigBlueButtonEvent(voiceConf, new UserConnectedToGlobalAudio(voiceConf, voiceConf, userid, name)))
|
||||
}
|
||||
|
||||
def userDisconnectedFromGlobalAudio(voiceConf: String, userid: String, name: String) {
|
||||
// we are required to pass the meeting_id as first parameter (just to satisfy trait)
|
||||
// but it's not used anywhere. That's why we pass voiceConf twice instead
|
||||
bbbActor ! new UserDisconnectedFromGlobalAudio(voiceConf, voiceConf, userid, name)
|
||||
eventBus.publish(BigBlueButtonEvent(voiceConf, new UserDisconnectedFromGlobalAudio(voiceConf, voiceConf, userid, name)))
|
||||
}
|
||||
|
||||
/**
|
||||
@ -219,19 +269,19 @@ class BigBlueButtonInGW(val system: ActorSystem, recorderApp: RecorderApplicatio
|
||||
*/
|
||||
|
||||
def clear(meetingID: String) {
|
||||
bbbActor ! new ClearPresentation(meetingID)
|
||||
eventBus.publish(BigBlueButtonEvent(meetingID, new ClearPresentation(meetingID)))
|
||||
}
|
||||
|
||||
def sendConversionUpdate(messageKey: String, meetingId: String, code: String, presentationId: String, presName: String) {
|
||||
bbbActor ! new PresentationConversionUpdate(meetingId, messageKey, code, presentationId, presName)
|
||||
eventBus.publish(BigBlueButtonEvent(meetingId, new PresentationConversionUpdate(meetingId, messageKey, code, presentationId, presName)))
|
||||
}
|
||||
|
||||
def sendPageCountError(messageKey: String, meetingId: String, code: String, presentationId: String, numberOfPages: Int, maxNumberPages: Int, presName: String) {
|
||||
bbbActor ! new PresentationPageCountError(meetingId, messageKey, code, presentationId, numberOfPages, maxNumberPages, presName)
|
||||
eventBus.publish(BigBlueButtonEvent(meetingId, new PresentationPageCountError(meetingId, messageKey, code, presentationId, numberOfPages, maxNumberPages, presName)))
|
||||
}
|
||||
|
||||
def sendSlideGenerated(messageKey: String, meetingId: String, code: String, presentationId: String, numberOfPages: Int, pagesCompleted: Int, presName: String) {
|
||||
bbbActor ! new PresentationSlideGenerated(meetingId, messageKey, code, presentationId, numberOfPages, pagesCompleted, presName)
|
||||
eventBus.publish(BigBlueButtonEvent(meetingId, new PresentationSlideGenerated(meetingId, messageKey, code, presentationId, numberOfPages, pagesCompleted, presName)))
|
||||
}
|
||||
|
||||
def generatePresentationPages(presId: String, numPages: Int, presBaseUrl: String): scala.collection.immutable.HashMap[String, Page] = {
|
||||
@ -260,37 +310,37 @@ class BigBlueButtonInGW(val system: ActorSystem, recorderApp: RecorderApplicatio
|
||||
|
||||
val pages = generatePresentationPages(presentationId, numPages, presBaseUrl)
|
||||
val presentation = new Presentation(id = presentationId, name = presName, pages = pages)
|
||||
bbbActor ! new PresentationConversionCompleted(meetingId, messageKey, code, presentation)
|
||||
eventBus.publish(BigBlueButtonEvent(meetingId, new PresentationConversionCompleted(meetingId, messageKey, code, presentation)))
|
||||
|
||||
}
|
||||
|
||||
def removePresentation(meetingID: String, presentationID: String) {
|
||||
bbbActor ! new RemovePresentation(meetingID, presentationID)
|
||||
eventBus.publish(BigBlueButtonEvent(meetingID, new RemovePresentation(meetingID, presentationID)))
|
||||
}
|
||||
|
||||
def getPresentationInfo(meetingID: String, requesterID: String, replyTo: String) {
|
||||
bbbActor ! new GetPresentationInfo(meetingID, requesterID, replyTo)
|
||||
eventBus.publish(BigBlueButtonEvent(meetingID, new GetPresentationInfo(meetingID, requesterID, replyTo)))
|
||||
}
|
||||
|
||||
def sendCursorUpdate(meetingID: String, xPercent: Double, yPercent: Double) {
|
||||
bbbActor ! new SendCursorUpdate(meetingID, xPercent, yPercent)
|
||||
eventBus.publish(BigBlueButtonEvent(meetingID, new SendCursorUpdate(meetingID, xPercent, yPercent)))
|
||||
}
|
||||
|
||||
def resizeAndMoveSlide(meetingID: String, xOffset: Double, yOffset: Double, widthRatio: Double, heightRatio: Double) {
|
||||
bbbActor ! new ResizeAndMoveSlide(meetingID, xOffset, yOffset, widthRatio, heightRatio)
|
||||
eventBus.publish(BigBlueButtonEvent(meetingID, new ResizeAndMoveSlide(meetingID, xOffset, yOffset, widthRatio, heightRatio)))
|
||||
}
|
||||
|
||||
def gotoSlide(meetingID: String, pageId: String) {
|
||||
// println("**** Forwarding GotoSlide for meeting[" + meetingID + "] ****")
|
||||
bbbActor ! new GotoSlide(meetingID, pageId)
|
||||
eventBus.publish(BigBlueButtonEvent(meetingID, new GotoSlide(meetingID, pageId)))
|
||||
}
|
||||
|
||||
def sharePresentation(meetingID: String, presentationID: String, share: Boolean) {
|
||||
bbbActor ! new SharePresentation(meetingID, presentationID, share)
|
||||
eventBus.publish(BigBlueButtonEvent(meetingID, new SharePresentation(meetingID, presentationID, share)))
|
||||
}
|
||||
|
||||
def getSlideInfo(meetingID: String, requesterID: String, replyTo: String) {
|
||||
bbbActor ! new GetSlideInfo(meetingID, requesterID, replyTo)
|
||||
eventBus.publish(BigBlueButtonEvent(meetingID, new GetSlideInfo(meetingID, requesterID, replyTo)))
|
||||
}
|
||||
|
||||
/**
|
||||
@ -300,18 +350,18 @@ class BigBlueButtonInGW(val system: ActorSystem, recorderApp: RecorderApplicatio
|
||||
*/
|
||||
|
||||
def getCurrentLayout(meetingID: String, requesterID: String) {
|
||||
bbbActor ! new GetCurrentLayoutRequest(meetingID, requesterID)
|
||||
eventBus.publish(BigBlueButtonEvent(meetingID, new GetCurrentLayoutRequest(meetingID, requesterID)))
|
||||
}
|
||||
|
||||
def broadcastLayout(meetingID: String, requesterID: String, layout: String) {
|
||||
bbbActor ! new BroadcastLayoutRequest(meetingID, requesterID, layout)
|
||||
eventBus.publish(BigBlueButtonEvent(meetingID, new BroadcastLayoutRequest(meetingID, requesterID, layout)))
|
||||
}
|
||||
|
||||
def lockLayout(meetingId: String, setById: String, lock: Boolean, viewersOnly: Boolean, layout: String) {
|
||||
if (layout != null) {
|
||||
bbbActor ! new LockLayoutRequest(meetingId, setById, lock, viewersOnly, Some(layout))
|
||||
eventBus.publish(BigBlueButtonEvent(meetingId, new LockLayoutRequest(meetingId, setById, lock, viewersOnly, Some(layout))))
|
||||
} else {
|
||||
bbbActor ! new LockLayoutRequest(meetingId, setById, lock, viewersOnly, None)
|
||||
eventBus.publish(BigBlueButtonEvent(meetingId, new LockLayoutRequest(meetingId, setById, lock, viewersOnly, None)))
|
||||
}
|
||||
|
||||
}
|
||||
@ -323,16 +373,16 @@ class BigBlueButtonInGW(val system: ActorSystem, recorderApp: RecorderApplicatio
|
||||
*/
|
||||
|
||||
def getChatHistory(meetingID: String, requesterID: String, replyTo: String) {
|
||||
bbbActor ! new GetChatHistoryRequest(meetingID, requesterID, replyTo)
|
||||
eventBus.publish(BigBlueButtonEvent(meetingID, new GetChatHistoryRequest(meetingID, requesterID, replyTo)))
|
||||
}
|
||||
|
||||
def sendPublicMessage(meetingID: String, requesterID: String, message: java.util.Map[String, String]) {
|
||||
// Convert java Map to Scala Map, then convert Mutable map to immutable map
|
||||
bbbActor ! new SendPublicMessageRequest(meetingID, requesterID, mapAsScalaMap(message).toMap)
|
||||
eventBus.publish(BigBlueButtonEvent(meetingID, new SendPublicMessageRequest(meetingID, requesterID, mapAsScalaMap(message).toMap)))
|
||||
}
|
||||
|
||||
def sendPrivateMessage(meetingID: String, requesterID: String, message: java.util.Map[String, String]) {
|
||||
bbbActor ! new SendPrivateMessageRequest(meetingID, requesterID, mapAsScalaMap(message).toMap)
|
||||
eventBus.publish(BigBlueButtonEvent(meetingID, new SendPrivateMessageRequest(meetingID, requesterID, mapAsScalaMap(message).toMap)))
|
||||
}
|
||||
|
||||
/**
|
||||
@ -361,30 +411,30 @@ class BigBlueButtonInGW(val system: ActorSystem, recorderApp: RecorderApplicatio
|
||||
|
||||
buildAnnotation(ann) match {
|
||||
case Some(shape) => {
|
||||
bbbActor ! new SendWhiteboardAnnotationRequest(meetingID, requesterID, shape)
|
||||
eventBus.publish(BigBlueButtonEvent(meetingID, new SendWhiteboardAnnotationRequest(meetingID, requesterID, shape)))
|
||||
}
|
||||
case None => // do nothing
|
||||
}
|
||||
}
|
||||
|
||||
def requestWhiteboardAnnotationHistory(meetingID: String, requesterID: String, whiteboardId: String, replyTo: String) {
|
||||
bbbActor ! new GetWhiteboardShapesRequest(meetingID, requesterID, whiteboardId, replyTo)
|
||||
eventBus.publish(BigBlueButtonEvent(meetingID, new GetWhiteboardShapesRequest(meetingID, requesterID, whiteboardId, replyTo)))
|
||||
}
|
||||
|
||||
def clearWhiteboard(meetingID: String, requesterID: String, whiteboardId: String) {
|
||||
bbbActor ! new ClearWhiteboardRequest(meetingID, requesterID, whiteboardId)
|
||||
eventBus.publish(BigBlueButtonEvent(meetingID, new ClearWhiteboardRequest(meetingID, requesterID, whiteboardId)))
|
||||
}
|
||||
|
||||
def undoWhiteboard(meetingID: String, requesterID: String, whiteboardId: String) {
|
||||
bbbActor ! new UndoWhiteboardRequest(meetingID, requesterID, whiteboardId)
|
||||
eventBus.publish(BigBlueButtonEvent(meetingID, new UndoWhiteboardRequest(meetingID, requesterID, whiteboardId)))
|
||||
}
|
||||
|
||||
def enableWhiteboard(meetingID: String, requesterID: String, enable: java.lang.Boolean) {
|
||||
bbbActor ! new EnableWhiteboardRequest(meetingID, requesterID, enable)
|
||||
eventBus.publish(BigBlueButtonEvent(meetingID, new EnableWhiteboardRequest(meetingID, requesterID, enable)))
|
||||
}
|
||||
|
||||
def isWhiteboardEnabled(meetingID: String, requesterID: String, replyTo: String) {
|
||||
bbbActor ! new IsWhiteboardEnabledRequest(meetingID, requesterID, replyTo)
|
||||
eventBus.publish(BigBlueButtonEvent(meetingID, new IsWhiteboardEnabledRequest(meetingID, requesterID, replyTo)))
|
||||
}
|
||||
|
||||
/**
|
||||
@ -394,55 +444,53 @@ class BigBlueButtonInGW(val system: ActorSystem, recorderApp: RecorderApplicatio
|
||||
*/
|
||||
|
||||
def muteAllExceptPresenter(meetingID: String, requesterID: String, mute: java.lang.Boolean) {
|
||||
bbbActor ! new MuteAllExceptPresenterRequest(meetingID, requesterID, mute)
|
||||
eventBus.publish(BigBlueButtonEvent(meetingID, new MuteAllExceptPresenterRequest(meetingID, requesterID, mute)))
|
||||
}
|
||||
|
||||
def muteAllUsers(meetingID: String, requesterID: String, mute: java.lang.Boolean) {
|
||||
bbbActor ! new MuteMeetingRequest(meetingID, requesterID, mute)
|
||||
eventBus.publish(BigBlueButtonEvent(meetingID, new MuteMeetingRequest(meetingID, requesterID, mute)))
|
||||
}
|
||||
|
||||
def isMeetingMuted(meetingID: String, requesterID: String) {
|
||||
bbbActor ! new IsMeetingMutedRequest(meetingID, requesterID)
|
||||
eventBus.publish(BigBlueButtonEvent(meetingID, new IsMeetingMutedRequest(meetingID, requesterID)))
|
||||
}
|
||||
|
||||
def muteUser(meetingID: String, requesterID: String, userID: String, mute: java.lang.Boolean) {
|
||||
bbbActor ! new MuteUserRequest(meetingID, requesterID, userID, mute)
|
||||
eventBus.publish(BigBlueButtonEvent(meetingID, new MuteUserRequest(meetingID, requesterID, userID, mute)))
|
||||
}
|
||||
|
||||
def lockMuteUser(meetingID: String, requesterID: String, userID: String, lock: java.lang.Boolean) {
|
||||
bbbActor ! new LockUserRequest(meetingID, requesterID, userID, lock)
|
||||
eventBus.publish(BigBlueButtonEvent(meetingID, new LockUserRequest(meetingID, requesterID, userID, lock)))
|
||||
}
|
||||
|
||||
def ejectUserFromVoice(meetingId: String, userId: String, ejectedBy: String) {
|
||||
bbbActor ! new EjectUserFromVoiceRequest(meetingId, userId, ejectedBy)
|
||||
eventBus.publish(BigBlueButtonEvent(meetingId, new EjectUserFromVoiceRequest(meetingId, userId, ejectedBy)))
|
||||
}
|
||||
|
||||
def voiceUserJoined(voiceConfId: String, voiceUserId: String, userId: String, callerIdName: String,
|
||||
callerIdNum: String, muted: java.lang.Boolean, avatarURL: String, talking: java.lang.Boolean) {
|
||||
|
||||
bbbActor ! new UserJoinedVoiceConfMessage(voiceConfId, voiceUserId, userId, userId, callerIdName,
|
||||
callerIdNum, muted, talking, avatarURL, false /*hardcode listenOnly to false as the message for listenOnly is ConnectedToGlobalAudio*/ )
|
||||
|
||||
eventBus.publish(BigBlueButtonEvent(voiceConfId, new UserJoinedVoiceConfMessage(voiceConfId, voiceUserId, userId, userId, callerIdName,
|
||||
callerIdNum, muted, talking, avatarURL, false /*hardcode listenOnly to false as the message for listenOnly is ConnectedToGlobalAudio*/ )))
|
||||
}
|
||||
|
||||
def voiceUserLeft(voiceConfId: String, voiceUserId: String) {
|
||||
bbbActor ! new UserLeftVoiceConfMessage(voiceConfId, voiceUserId)
|
||||
eventBus.publish(BigBlueButtonEvent(voiceConfId, new UserLeftVoiceConfMessage(voiceConfId, voiceUserId)))
|
||||
}
|
||||
|
||||
def voiceUserLocked(voiceConfId: String, voiceUserId: String, locked: java.lang.Boolean) {
|
||||
bbbActor ! new UserLockedInVoiceConfMessage(voiceConfId, voiceUserId, locked)
|
||||
eventBus.publish(BigBlueButtonEvent(voiceConfId, new UserLockedInVoiceConfMessage(voiceConfId, voiceUserId, locked)))
|
||||
}
|
||||
|
||||
def voiceUserMuted(voiceConfId: String, voiceUserId: String, muted: java.lang.Boolean) {
|
||||
bbbActor ! new UserMutedInVoiceConfMessage(voiceConfId, voiceUserId, muted)
|
||||
eventBus.publish(BigBlueButtonEvent(voiceConfId, new UserMutedInVoiceConfMessage(voiceConfId, voiceUserId, muted)))
|
||||
}
|
||||
|
||||
def voiceUserTalking(voiceConfId: String, voiceUserId: String, talking: java.lang.Boolean) {
|
||||
bbbActor ! new UserTalkingInVoiceConfMessage(voiceConfId, voiceUserId, talking)
|
||||
eventBus.publish(BigBlueButtonEvent(voiceConfId, new UserTalkingInVoiceConfMessage(voiceConfId, voiceUserId, talking)))
|
||||
}
|
||||
|
||||
def voiceRecording(voiceConfId: String, recordingFile: String, timestamp: String, recording: java.lang.Boolean) {
|
||||
bbbActor ! new VoiceConfRecordingStartedMessage(voiceConfId, recordingFile, recording, timestamp)
|
||||
eventBus.publish(BigBlueButtonEvent(voiceConfId, new VoiceConfRecordingStartedMessage(voiceConfId, recordingFile, recording, timestamp)))
|
||||
}
|
||||
|
||||
/**
|
||||
@ -450,44 +498,62 @@ class BigBlueButtonInGW(val system: ActorSystem, recorderApp: RecorderApplicatio
|
||||
* Message Interface for DeskShare
|
||||
* *****************************************************************
|
||||
*/
|
||||
def deskShareStarted(conferenceName: String, callerId: String, callerIdName: String) {
|
||||
bbbActor ! new DeskShareStartedRequest(conferenceName, callerId, callerIdName)
|
||||
def deskShareStarted(meetingId: String, callerId: String, callerIdName: String) {
|
||||
eventBus.publish(BigBlueButtonEvent(meetingId, new DeskShareStartedRequest(meetingId, callerId, callerIdName)))
|
||||
}
|
||||
|
||||
def deskShareStopped(conferenceName: String, callerId: String, callerIdName: String) {
|
||||
bbbActor ! new DeskShareStoppedRequest(conferenceName, callerId, callerIdName)
|
||||
def deskShareStopped(meetingId: String, callerId: String, callerIdName: String) {
|
||||
eventBus.publish(BigBlueButtonEvent(meetingId, new DeskShareStoppedRequest(meetingId, callerId, callerIdName)))
|
||||
}
|
||||
|
||||
def deskShareRTMPBroadcastStarted(conferenceName: String, streamname: String, videoWidth: Int, videoHeight: Int, timestamp: String) {
|
||||
bbbActor ! new DeskShareRTMPBroadcastStartedRequest(conferenceName, streamname, videoWidth, videoHeight, timestamp)
|
||||
def deskShareRTMPBroadcastStarted(meetingId: String, streamname: String, videoWidth: Int, videoHeight: Int, timestamp: String) {
|
||||
eventBus.publish(BigBlueButtonEvent(meetingId, new DeskShareRTMPBroadcastStartedRequest(meetingId, streamname, videoWidth, videoHeight, timestamp)))
|
||||
}
|
||||
|
||||
def deskShareRTMPBroadcastStopped(conferenceName: String, streamname: String, videoWidth: Int, videoHeight: Int, timestamp: String) {
|
||||
bbbActor ! new DeskShareRTMPBroadcastStoppedRequest(conferenceName, streamname, videoWidth, videoHeight, timestamp)
|
||||
def deskShareRTMPBroadcastStopped(meetingId: String, streamname: String, videoWidth: Int, videoHeight: Int, timestamp: String) {
|
||||
eventBus.publish(BigBlueButtonEvent(meetingId, new DeskShareRTMPBroadcastStoppedRequest(meetingId, streamname, videoWidth, videoHeight, timestamp)))
|
||||
}
|
||||
|
||||
def deskShareGetInfoRequest(meetingId: String, requesterId: String, replyTo: String): Unit = {
|
||||
bbbActor ! new DeskShareGetDeskShareInfoRequest(meetingId, requesterId, replyTo)
|
||||
eventBus.publish(BigBlueButtonEvent(meetingId, new DeskShareGetDeskShareInfoRequest(meetingId, requesterId, replyTo)))
|
||||
}
|
||||
|
||||
// Polling
|
||||
def votePoll(meetingId: String, userId: String, pollId: String, questionId: Integer, answerId: Integer) {
|
||||
bbbActor ! new RespondToPollRequest(meetingId, userId, pollId, questionId, answerId)
|
||||
eventBus.publish(BigBlueButtonEvent(meetingId, new RespondToPollRequest(meetingId, userId, pollId, questionId, answerId)))
|
||||
}
|
||||
|
||||
def startPoll(meetingId: String, requesterId: String, pollId: String, pollType: String) {
|
||||
bbbActor ! new StartPollRequest(meetingId, requesterId, pollType)
|
||||
eventBus.publish(BigBlueButtonEvent(meetingId, new StartPollRequest(meetingId, requesterId, pollType)))
|
||||
}
|
||||
|
||||
def stopPoll(meetingId: String, userId: String, pollId: String) {
|
||||
bbbActor ! new StopPollRequest(meetingId, userId)
|
||||
eventBus.publish(BigBlueButtonEvent(meetingId, new StopPollRequest(meetingId, userId)))
|
||||
}
|
||||
|
||||
def showPollResult(meetingId: String, requesterId: String, pollId: String, show: java.lang.Boolean) {
|
||||
if (show) {
|
||||
bbbActor ! new ShowPollResultRequest(meetingId, requesterId, pollId)
|
||||
eventBus.publish(BigBlueButtonEvent(meetingId, new ShowPollResultRequest(meetingId, requesterId, pollId)))
|
||||
} else {
|
||||
bbbActor ! new HidePollResultRequest(meetingId, requesterId, pollId)
|
||||
eventBus.publish(BigBlueButtonEvent(meetingId, new HidePollResultRequest(meetingId, requesterId, pollId)))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* *******************************************************************
|
||||
* Message Interface for Caption
|
||||
* *****************************************************************
|
||||
*/
|
||||
|
||||
def sendCaptionHistory(meetingID: String, requesterID: String) {
|
||||
eventBus.publish(BigBlueButtonEvent(meetingID, new SendCaptionHistoryRequest(meetingID, requesterID)))
|
||||
}
|
||||
|
||||
def updateCaptionOwner(meetingID: String, locale: String, ownerID: String) {
|
||||
eventBus.publish(BigBlueButtonEvent(meetingID, new UpdateCaptionOwnerRequest(meetingID, locale, ownerID)))
|
||||
}
|
||||
|
||||
def editCaptionHistory(meetingID: String, userID: String, startIndex: Integer, endIndex: Integer, locale: String, text: String) {
|
||||
eventBus.publish(BigBlueButtonEvent(meetingID, new EditCaptionHistoryRequest(meetingID, userID, startIndex, endIndex, locale, text)))
|
||||
}
|
||||
}
|
||||
|
9
akka-bbb-apps/src/main/scala/org/bigbluebutton/core/FooTypeBar.scala
Executable file
9
akka-bbb-apps/src/main/scala/org/bigbluebutton/core/FooTypeBar.scala
Executable file
@ -0,0 +1,9 @@
|
||||
package org.bigbluebutton.core {
|
||||
|
||||
object MessageType extends scala.Enumeration {
|
||||
type MessageType = Value
|
||||
val SYSTEM = Value("system")
|
||||
val BROADCAST = Value("broadcast")
|
||||
val DIRECT = Value("direct")
|
||||
}
|
||||
}
|
78
akka-bbb-apps/src/main/scala/org/bigbluebutton/core/JsonMessageDecoder.scala
Executable file
78
akka-bbb-apps/src/main/scala/org/bigbluebutton/core/JsonMessageDecoder.scala
Executable file
@ -0,0 +1,78 @@
|
||||
package org.bigbluebutton.core
|
||||
|
||||
import scala.util.{ Try, Success, Failure }
|
||||
import spray.json.{ JsObject, JsonParser, DeserializationException }
|
||||
import org.parboiled.errors.ParsingException
|
||||
import org.bigbluebutton.core.api._
|
||||
import org.bigbluebutton.messages._
|
||||
|
||||
object JsonMessageDecoder {
|
||||
import org.bigbluebutton.core.UserMessagesProtocol._
|
||||
import spray.json._
|
||||
|
||||
def header(msg: JsObject): InMessageHeader = {
|
||||
msg.fields.get("header") match {
|
||||
case Some(header) =>
|
||||
header.convertTo[InMessageHeader]
|
||||
case None =>
|
||||
throw MessageProcessException("Cannot get payload information: [" + msg + "]")
|
||||
}
|
||||
}
|
||||
|
||||
def payload(msg: JsObject): JsObject = {
|
||||
msg.fields.get("payload") match {
|
||||
case Some(payload) =>
|
||||
payload.asJsObject
|
||||
case None =>
|
||||
throw MessageProcessException("Cannot get payload information: [" + msg + "]")
|
||||
}
|
||||
}
|
||||
|
||||
def toJsObject(msg: String): JsObject = {
|
||||
try {
|
||||
JsonParser(msg).asJsObject
|
||||
} catch {
|
||||
case e: ParsingException => {
|
||||
throw MessageProcessException("Cannot parse JSON message: [" + msg + "]")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def unmarshall(jsonMsg: String): Try[InMessage] = {
|
||||
for {
|
||||
jsonObj <- Try(toJsObject(jsonMsg))
|
||||
header <- Try(header(jsonObj))
|
||||
payload <- Try(payload(jsonObj))
|
||||
msg = InHeaderAndJsonPayload(header, payload)
|
||||
inmsg <- Try(convertMessage(msg))
|
||||
} yield inmsg
|
||||
}
|
||||
|
||||
def decode(json: String): Option[InMessage] = {
|
||||
unmarshall(json) match {
|
||||
case Success(validMsg) => Some(validMsg)
|
||||
case Failure(ex) => None
|
||||
}
|
||||
}
|
||||
|
||||
def convertMessage(msg: InHeaderAndJsonPayload): InMessage = {
|
||||
msg.header.name match {
|
||||
case GetBreakoutRoomsList.NAME => {
|
||||
msg.payload.convertTo[BreakoutRoomsListMessage]
|
||||
}
|
||||
case CreateBreakoutRoomsRequest.NAME => {
|
||||
msg.payload.convertTo[CreateBreakoutRooms]
|
||||
}
|
||||
case RequestBreakoutJoinURL.NAME => {
|
||||
msg.payload.convertTo[RequestBreakoutJoinURLInMessage]
|
||||
}
|
||||
case ListenInOnBreakout.NAME => {
|
||||
msg.payload.convertTo[TransferUserToMeetingRequest]
|
||||
}
|
||||
case EndAllBreakoutRoomsRequest.NAME => {
|
||||
msg.payload.convertTo[EndAllBreakoutRooms]
|
||||
}
|
||||
case _ => throw MessageProcessException("Cannot parse JSON message: [" + msg + "]")
|
||||
}
|
||||
}
|
||||
}
|
114
akka-bbb-apps/src/main/scala/org/bigbluebutton/core/JsonMessageSenderActor.scala
Executable file
114
akka-bbb-apps/src/main/scala/org/bigbluebutton/core/JsonMessageSenderActor.scala
Executable file
@ -0,0 +1,114 @@
|
||||
package org.bigbluebutton.core
|
||||
|
||||
import akka.actor.Actor
|
||||
import akka.actor.ActorRef
|
||||
import akka.actor.ActorLogging
|
||||
import akka.actor.Props
|
||||
import org.bigbluebutton.core.api._
|
||||
import org.bigbluebutton.common.messages.MessagingConstants
|
||||
import org.bigbluebutton.core.pubsub.senders.ChatMessageToJsonConverter
|
||||
import org.bigbluebutton.common.messages.StartRecordingVoiceConfRequestMessage
|
||||
import org.bigbluebutton.common.messages.StopRecordingVoiceConfRequestMessage
|
||||
import org.bigbluebutton.core.pubsub.senders.MeetingMessageToJsonConverter
|
||||
import org.bigbluebutton.core.pubsub.senders.PesentationMessageToJsonConverter
|
||||
import org.bigbluebutton.common.messages.GetPresentationInfoReplyMessage
|
||||
import org.bigbluebutton.common.messages.PresentationRemovedMessage
|
||||
import org.bigbluebutton.core.apps.Page
|
||||
import collection.JavaConverters._
|
||||
import scala.collection.JavaConversions._
|
||||
import org.bigbluebutton.core.apps.SimplePollResultOutVO
|
||||
import org.bigbluebutton.core.apps.SimplePollOutVO
|
||||
import org.bigbluebutton.core.pubsub.senders.UsersMessageToJsonConverter
|
||||
import org.bigbluebutton.common.messages._
|
||||
import org.bigbluebutton.core.pubsub.senders.WhiteboardMessageToJsonConverter
|
||||
import org.bigbluebutton.common.converters.ToJsonEncoder
|
||||
import org.bigbluebutton.common.messages.payload._
|
||||
import org.bigbluebutton.common.messages._
|
||||
import org.bigbluebutton.messages.payload._
|
||||
import org.bigbluebutton.messages._
|
||||
|
||||
object JsonMessageSenderActor {
|
||||
def props(msgSender: MessageSender): Props =
|
||||
Props(classOf[JsonMessageSenderActor], msgSender)
|
||||
}
|
||||
|
||||
class JsonMessageSenderActor(val service: MessageSender)
|
||||
extends Actor with ActorLogging {
|
||||
|
||||
def receive = {
|
||||
|
||||
// Breakout
|
||||
case msg: CreateBreakoutRoom => handleCreateBreakoutRoom(msg)
|
||||
case msg: EndBreakoutRoom => handleEndBreakoutRoom(msg)
|
||||
case msg: BreakoutRoomsListOutMessage => handleBreakoutRoomsList(msg)
|
||||
case msg: BreakoutRoomJoinURLOutMessage => handleBreakoutRoomJoinURL(msg)
|
||||
case msg: BreakoutRoomStartedOutMessage => handleBreakoutRoomStarted(msg)
|
||||
case msg: BreakoutRoomEndedOutMessage => handleBreakoutRoomEnded(msg)
|
||||
case msg: UpdateBreakoutUsersOutMessage => handleUpdateBreakoutUsers(msg)
|
||||
case msg: MeetingTimeRemainingUpdate => handleMeetingTimeRemainingUpdate(msg)
|
||||
|
||||
case _ => // do nothing
|
||||
}
|
||||
|
||||
// Breakout
|
||||
private def handleBreakoutRoomStarted(msg: BreakoutRoomStartedOutMessage) {
|
||||
val payload = new BreakoutRoomPayload(msg.meetingId, msg.breakout.breakoutId, msg.breakout.name)
|
||||
val request = new BreakoutRoomStarted(payload)
|
||||
service.send(MessagingConstants.FROM_MEETING_CHANNEL, request.toJson)
|
||||
}
|
||||
|
||||
private def handleBreakoutRoomEnded(msg: BreakoutRoomEndedOutMessage) {
|
||||
val payload = new BreakoutRoomPayload(msg.meetingId, msg.breakoutId, "")
|
||||
val request = new BreakoutRoomClosed(payload)
|
||||
service.send(MessagingConstants.FROM_MEETING_CHANNEL, request.toJson)
|
||||
}
|
||||
|
||||
private def handleUpdateBreakoutUsers(msg: UpdateBreakoutUsersOutMessage) {
|
||||
val users = new java.util.ArrayList[BreakoutUserPayload]()
|
||||
msg.users.foreach(x => users.add(new BreakoutUserPayload(x.id, x.name)))
|
||||
val payload = new UpdateBreakoutUsersPayload(msg.meetingId, msg.breakoutId, users)
|
||||
val request = new UpdateBreakoutUsers(payload)
|
||||
service.send(MessagingConstants.FROM_MEETING_CHANNEL, request.toJson())
|
||||
}
|
||||
|
||||
private def handleMeetingTimeRemainingUpdate(msg: MeetingTimeRemainingUpdate) {
|
||||
val payload = new MeetingTimeRemainingPayload(msg.meetingId, msg.timeRemaining)
|
||||
val request = new TimeRemainingUpdate(payload)
|
||||
service.send(MessagingConstants.FROM_MEETING_CHANNEL, request.toJson())
|
||||
}
|
||||
|
||||
private def handleMeetingTimeRemainingUpdate(msg: BreakoutRoomsTimeRemainingUpdateOutMessage) {
|
||||
val payload = new BreakoutRoomsTimeRemainingPayload(msg.meetingId, msg.timeRemaining)
|
||||
val request = new BreakoutRoomsTimeRemainingUpdate(payload)
|
||||
service.send(MessagingConstants.FROM_MEETING_CHANNEL, request.toJson())
|
||||
}
|
||||
|
||||
private def handleBreakoutRoomsList(msg: BreakoutRoomsListOutMessage) {
|
||||
val rooms = new java.util.ArrayList[BreakoutRoomPayload]()
|
||||
msg.rooms.foreach(r => rooms.add(new BreakoutRoomPayload(msg.meetingId, r.breakoutId, r.name)))
|
||||
val payload = new BreakoutRoomsListPayload(msg.meetingId, rooms)
|
||||
val request = new BreakoutRoomsList(payload)
|
||||
service.send(MessagingConstants.FROM_MEETING_CHANNEL, request.toJson())
|
||||
}
|
||||
|
||||
private def handleCreateBreakoutRoom(msg: CreateBreakoutRoom) {
|
||||
val payload = new CreateBreakoutRoomRequestPayload(msg.room.breakoutId, msg.room.parentId, msg.room.name,
|
||||
msg.room.voiceConfId, msg.room.viewerPassword, msg.room.moderatorPassword,
|
||||
msg.room.durationInMinutes, msg.room.defaultPresentationURL)
|
||||
val request = new CreateBreakoutRoomRequest(payload)
|
||||
service.send(MessagingConstants.FROM_MEETING_CHANNEL, request.toJson())
|
||||
}
|
||||
|
||||
private def handleEndBreakoutRoom(msg: EndBreakoutRoom) {
|
||||
val payload = new EndBreakoutRoomRequestPayload(msg.breakoutId)
|
||||
val request = new EndBreakoutRoomRequest(payload)
|
||||
service.send(MessagingConstants.FROM_MEETING_CHANNEL, request.toJson())
|
||||
}
|
||||
|
||||
def handleBreakoutRoomJoinURL(msg: BreakoutRoomJoinURLOutMessage) {
|
||||
val payload = new BreakoutRoomJoinURLPayload(msg.meetingId,
|
||||
msg.breakoutId, msg.userId, msg.joinURL)
|
||||
val request = new BreakoutRoomJoinURL(payload)
|
||||
service.send(MessagingConstants.FROM_MEETING_CHANNEL, request.toJson)
|
||||
}
|
||||
}
|
160
akka-bbb-apps/src/main/scala/org/bigbluebutton/core/LiveMeeting.scala
Executable file
160
akka-bbb-apps/src/main/scala/org/bigbluebutton/core/LiveMeeting.scala
Executable file
@ -0,0 +1,160 @@
|
||||
package org.bigbluebutton.core
|
||||
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
import org.bigbluebutton.core.api._
|
||||
import org.bigbluebutton.core.apps._
|
||||
import org.bigbluebutton.core.bus.IncomingEventBus
|
||||
|
||||
import akka.actor.ActorContext
|
||||
import akka.event.Logging
|
||||
import org.bigbluebutton.core.apps.CaptionApp
|
||||
import org.bigbluebutton.core.apps.CaptionModel
|
||||
|
||||
class LiveMeeting(val mProps: MeetingProperties,
|
||||
val eventBus: IncomingEventBus,
|
||||
val outGW: OutMessageGateway,
|
||||
val chatModel: ChatModel,
|
||||
val layoutModel: LayoutModel,
|
||||
val meetingModel: MeetingModel,
|
||||
val usersModel: UsersModel,
|
||||
val pollModel: PollModel,
|
||||
val wbModel: WhiteboardModel,
|
||||
val presModel: PresentationModel,
|
||||
val breakoutModel: BreakoutRoomModel,
|
||||
val captionModel: CaptionModel)(implicit val context: ActorContext)
|
||||
extends UsersApp with PresentationApp
|
||||
with LayoutApp with ChatApp with WhiteboardApp with PollApp
|
||||
with BreakoutRoomApp with CaptionApp {
|
||||
|
||||
val log = Logging(context.system, getClass)
|
||||
|
||||
def hasMeetingEnded(): Boolean = {
|
||||
meetingModel.hasMeetingEnded()
|
||||
}
|
||||
|
||||
def webUserJoined() {
|
||||
if (usersModel.numWebUsers > 0) {
|
||||
meetingModel.resetLastWebUserLeftOn()
|
||||
}
|
||||
}
|
||||
|
||||
def startRecordingIfAutoStart() {
|
||||
if (mProps.recorded && !meetingModel.isRecording() && mProps.autoStartRecording && usersModel.numWebUsers == 1) {
|
||||
log.info("Auto start recording. meetingId={}", mProps.meetingID)
|
||||
meetingModel.recordingStarted()
|
||||
outGW.send(new RecordingStatusChanged(mProps.meetingID, mProps.recorded, "system", meetingModel.isRecording()))
|
||||
}
|
||||
}
|
||||
|
||||
def stopAutoStartedRecording() {
|
||||
if (mProps.recorded && meetingModel.isRecording() && mProps.autoStartRecording && usersModel.numWebUsers == 0) {
|
||||
log.info("Last web user left. Auto stopping recording. meetingId={}", mProps.meetingID)
|
||||
meetingModel.recordingStopped()
|
||||
outGW.send(new RecordingStatusChanged(mProps.meetingID, mProps.recorded, "system", meetingModel.isRecording()))
|
||||
}
|
||||
}
|
||||
|
||||
def startCheckingIfWeNeedToEndVoiceConf() {
|
||||
if (usersModel.numWebUsers == 0) {
|
||||
meetingModel.lastWebUserLeft()
|
||||
log.debug("MonitorNumberOfWebUsers started for meeting [" + mProps.meetingID + "]")
|
||||
}
|
||||
}
|
||||
|
||||
def sendTimeRemainingNotice() {
|
||||
val now = timeNowInSeconds
|
||||
|
||||
if (mProps.duration > 0 && (((meetingModel.startedOn + mProps.duration) - now) < 15)) {
|
||||
// log.warning("MEETING WILL END IN 15 MINUTES!!!!")
|
||||
}
|
||||
}
|
||||
|
||||
def handleMonitorNumberOfWebUsers(msg: MonitorNumberOfUsers) {
|
||||
if (usersModel.numWebUsers == 0 && meetingModel.lastWebUserLeftOn > 0) {
|
||||
if (timeNowInMinutes - meetingModel.lastWebUserLeftOn > 2) {
|
||||
log.info("Empty meeting. Ejecting all users from voice. meetingId={}", mProps.meetingID)
|
||||
outGW.send(new EjectAllVoiceUsers(mProps.meetingID, mProps.recorded, mProps.voiceBridge))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def handleSendTimeRemainingUpdate(msg: SendTimeRemainingUpdate) {
|
||||
if (mProps.duration > 0) {
|
||||
val endMeetingTime = meetingModel.startedOn + (mProps.duration * 60)
|
||||
val timeRemaining = endMeetingTime - timeNowInSeconds
|
||||
outGW.send(new MeetingTimeRemainingUpdate(mProps.meetingID, mProps.recorded, timeRemaining.toInt))
|
||||
}
|
||||
if (!mProps.isBreakout && breakoutModel.getRooms().length > 0) {
|
||||
val room = breakoutModel.getRooms()(0);
|
||||
val endMeetingTime = meetingModel.breakoutRoomsStartedOn + (meetingModel.breakoutRoomsdurationInMinutes * 60)
|
||||
val timeRemaining = endMeetingTime - timeNowInSeconds
|
||||
outGW.send(new BreakoutRoomsTimeRemainingUpdateOutMessage(mProps.meetingID, mProps.recorded, timeRemaining.toInt))
|
||||
} else if (meetingModel.breakoutRoomsStartedOn != 0) {
|
||||
meetingModel.breakoutRoomsdurationInMinutes = 0;
|
||||
meetingModel.breakoutRoomsStartedOn = 0;
|
||||
}
|
||||
}
|
||||
|
||||
def handleExtendMeetingDuration(msg: ExtendMeetingDuration) {
|
||||
|
||||
}
|
||||
|
||||
def timeNowInMinutes(): Long = {
|
||||
TimeUnit.NANOSECONDS.toMinutes(System.nanoTime())
|
||||
}
|
||||
|
||||
def timeNowInSeconds(): Long = {
|
||||
TimeUnit.NANOSECONDS.toSeconds(System.nanoTime())
|
||||
}
|
||||
|
||||
def sendMeetingHasEnded(userId: String) {
|
||||
outGW.send(new MeetingHasEnded(mProps.meetingID, userId))
|
||||
outGW.send(new DisconnectUser(mProps.meetingID, userId))
|
||||
}
|
||||
|
||||
def handleEndMeeting(msg: EndMeeting) {
|
||||
meetingModel.meetingHasEnded
|
||||
outGW.send(new MeetingEnded(msg.meetingId, mProps.recorded, mProps.voiceBridge))
|
||||
outGW.send(new DisconnectAllUsers(msg.meetingId))
|
||||
}
|
||||
|
||||
def handleVoiceConfRecordingStartedMessage(msg: VoiceConfRecordingStartedMessage) {
|
||||
if (msg.recording) {
|
||||
meetingModel.setVoiceRecordingFilename(msg.recordStream)
|
||||
outGW.send(new VoiceRecordingStarted(mProps.meetingID, mProps.recorded, msg.recordStream, msg.timestamp, mProps.voiceBridge))
|
||||
} else {
|
||||
meetingModel.setVoiceRecordingFilename("")
|
||||
outGW.send(new VoiceRecordingStopped(mProps.meetingID, mProps.recorded, msg.recordStream, msg.timestamp, mProps.voiceBridge))
|
||||
}
|
||||
}
|
||||
|
||||
def handleSetRecordingStatus(msg: SetRecordingStatus) {
|
||||
log.info("Change recording status. meetingId=" + mProps.meetingID + " recording=" + msg.recording)
|
||||
if (mProps.allowStartStopRecording && meetingModel.isRecording() != msg.recording) {
|
||||
if (msg.recording) {
|
||||
meetingModel.recordingStarted()
|
||||
} else {
|
||||
meetingModel.recordingStopped()
|
||||
}
|
||||
|
||||
outGW.send(new RecordingStatusChanged(mProps.meetingID, mProps.recorded, msg.userId, msg.recording))
|
||||
}
|
||||
}
|
||||
|
||||
def handleGetRecordingStatus(msg: GetRecordingStatus) {
|
||||
outGW.send(new GetRecordingStatusReply(mProps.meetingID, mProps.recorded, msg.userId, meetingModel.isRecording().booleanValue()))
|
||||
}
|
||||
|
||||
def lockLayout(lock: Boolean) {
|
||||
meetingModel.lockLayout(lock)
|
||||
}
|
||||
|
||||
def newPermissions(np: Permissions) {
|
||||
meetingModel.setPermissions(np)
|
||||
}
|
||||
|
||||
def permissionsEqual(other: Permissions): Boolean = {
|
||||
meetingModel.permissionsEqual(other)
|
||||
}
|
||||
}
|
@ -4,23 +4,72 @@ import akka.actor.Actor
|
||||
import akka.actor.ActorRef
|
||||
import akka.actor.ActorLogging
|
||||
import akka.actor.Props
|
||||
import org.bigbluebutton.core.bus._
|
||||
import org.bigbluebutton.core.api._
|
||||
import java.util.concurrent.TimeUnit
|
||||
import org.bigbluebutton.core.util._
|
||||
import scala.concurrent.duration._
|
||||
import org.bigbluebutton.core.apps.{ PollApp, UsersApp, PresentationApp, LayoutApp, ChatApp, WhiteboardApp }
|
||||
import org.bigbluebutton.core.apps.{ ChatModel, LayoutModel, UsersModel, PollModel, WhiteboardModel }
|
||||
import org.bigbluebutton.core.apps.{ PollApp, UsersApp, PresentationApp, LayoutApp, ChatApp, WhiteboardApp, CaptionApp }
|
||||
import org.bigbluebutton.core.apps.{ ChatModel, LayoutModel, UsersModel, PollModel, WhiteboardModel, CaptionModel }
|
||||
import org.bigbluebutton.core.apps.PresentationModel
|
||||
import org.bigbluebutton.core.apps.BreakoutRoomApp
|
||||
import org.bigbluebutton.core.apps.BreakoutRoomModel
|
||||
|
||||
object MeetingActor {
|
||||
def props(mProps: MeetingProperties, outGW: OutMessageGateway): Props =
|
||||
Props(classOf[MeetingActor], mProps: MeetingProperties, outGW)
|
||||
object MeetingActorInternal {
|
||||
def props(mProps: MeetingProperties,
|
||||
eventBus: IncomingEventBus,
|
||||
outGW: OutMessageGateway): Props =
|
||||
Props(classOf[MeetingActorInternal], mProps, eventBus, outGW)
|
||||
}
|
||||
|
||||
class MeetingActor(val mProps: MeetingProperties, val outGW: OutMessageGateway)
|
||||
extends Actor with UsersApp with PresentationApp
|
||||
with LayoutApp with ChatApp with WhiteboardApp with PollApp
|
||||
with ActorLogging {
|
||||
// This actor is an internal audit actor for each meeting actor that
|
||||
// periodically sends messages to the meeting actor
|
||||
class MeetingActorInternal(val mProps: MeetingProperties,
|
||||
val eventBus: IncomingEventBus, val outGW: OutMessageGateway)
|
||||
extends Actor with ActorLogging {
|
||||
|
||||
import context.dispatcher
|
||||
context.system.scheduler.schedule(2 seconds, 30 seconds, self, "MonitorNumberOfWebUsers")
|
||||
|
||||
// Query to get voice conference users
|
||||
outGW.send(new GetUsersInVoiceConference(mProps.meetingID, mProps.recorded, mProps.voiceBridge))
|
||||
|
||||
if (mProps.isBreakout) {
|
||||
// This is a breakout room. Inform our parent meeting that we have been successfully created.
|
||||
eventBus.publish(BigBlueButtonEvent(
|
||||
mProps.externalMeetingID,
|
||||
BreakoutRoomCreated(mProps.externalMeetingID, mProps.meetingID)))
|
||||
}
|
||||
|
||||
def receive = {
|
||||
case "MonitorNumberOfWebUsers" => handleMonitorNumberOfWebUsers()
|
||||
}
|
||||
|
||||
def handleMonitorNumberOfWebUsers() {
|
||||
eventBus.publish(BigBlueButtonEvent(mProps.meetingID, MonitorNumberOfUsers(mProps.meetingID)))
|
||||
|
||||
// Trigger updating users of time remaining on meeting.
|
||||
eventBus.publish(BigBlueButtonEvent(mProps.meetingID, SendTimeRemainingUpdate(mProps.meetingID)))
|
||||
|
||||
if (mProps.isBreakout) {
|
||||
// This is a breakout room. Update the main meeting with list of users in this breakout room.
|
||||
eventBus.publish(BigBlueButtonEvent(mProps.meetingID, SendBreakoutUsersUpdate(mProps.meetingID)))
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
object MeetingActor {
|
||||
def props(mProps: MeetingProperties,
|
||||
eventBus: IncomingEventBus,
|
||||
outGW: OutMessageGateway): Props =
|
||||
Props(classOf[MeetingActor], mProps, eventBus, outGW)
|
||||
}
|
||||
|
||||
class MeetingActor(val mProps: MeetingProperties,
|
||||
val eventBus: IncomingEventBus,
|
||||
val outGW: OutMessageGateway)
|
||||
extends Actor with ActorLogging {
|
||||
|
||||
val chatModel = new ChatModel()
|
||||
val layoutModel = new LayoutModel()
|
||||
@ -29,280 +78,109 @@ class MeetingActor(val mProps: MeetingProperties, val outGW: OutMessageGateway)
|
||||
val pollModel = new PollModel()
|
||||
val wbModel = new WhiteboardModel()
|
||||
val presModel = new PresentationModel()
|
||||
val breakoutModel = new BreakoutRoomModel()
|
||||
val captionModel = new CaptionModel()
|
||||
|
||||
import context.dispatcher
|
||||
context.system.scheduler.schedule(2 seconds, 30 seconds, self, "MonitorNumberOfWebUsers")
|
||||
// We extract the meeting handlers into this class so it is
|
||||
// easy to test.
|
||||
val liveMeeting = new LiveMeeting(mProps, eventBus, outGW,
|
||||
chatModel, layoutModel, meetingModel, usersModel, pollModel,
|
||||
wbModel, presModel, breakoutModel, captionModel)
|
||||
|
||||
outGW.send(new GetUsersInVoiceConference(mProps.meetingID, mProps.recorded, mProps.voiceBridge))
|
||||
/**
|
||||
* Put the internal message injector into another actor so this
|
||||
* actor is easy to test.
|
||||
*/
|
||||
var actorMonitor = context.actorOf(MeetingActorInternal.props(mProps, eventBus, outGW))
|
||||
|
||||
def receive = {
|
||||
case "StartTimer" =>
|
||||
handleStartTimer
|
||||
case "Hello" =>
|
||||
handleHello
|
||||
case "MonitorNumberOfWebUsers" =>
|
||||
handleMonitorNumberOfWebUsers()
|
||||
case msg: ValidateAuthToken =>
|
||||
handleValidateAuthToken(msg)
|
||||
case msg: RegisterUser =>
|
||||
handleRegisterUser(msg)
|
||||
case msg: UserJoinedVoiceConfMessage =>
|
||||
handleUserJoinedVoiceConfMessage(msg)
|
||||
case msg: UserLeftVoiceConfMessage =>
|
||||
handleUserLeftVoiceConfMessage(msg)
|
||||
case msg: UserMutedInVoiceConfMessage =>
|
||||
handleUserMutedInVoiceConfMessage(msg)
|
||||
case msg: UserTalkingInVoiceConfMessage =>
|
||||
handleUserTalkingInVoiceConfMessage(msg)
|
||||
case msg: VoiceConfRecordingStartedMessage =>
|
||||
handleVoiceConfRecordingStartedMessage(msg)
|
||||
case msg: DeskShareRTMPBroadcastStartedRequest =>
|
||||
handleDeskShareRTMPBroadcastStartedRequest(msg)
|
||||
case msg: DeskShareRTMPBroadcastStoppedRequest =>
|
||||
handleDeskShareRTMPBroadcastStoppedRequest(msg)
|
||||
case msg: DeskShareStartedRequest =>
|
||||
handleDeskShareStartedRequest(msg)
|
||||
case msg: DeskShareStoppedRequest =>
|
||||
handleDeskShareStoppedRequest(msg)
|
||||
case msg: DeskShareGetDeskShareInfoRequest =>
|
||||
handleDeskShareGetDeskShareInfoRequest(msg)
|
||||
case msg: UserJoining =>
|
||||
handleUserJoin(msg)
|
||||
case msg: UserLeaving =>
|
||||
handleUserLeft(msg)
|
||||
case msg: AssignPresenter =>
|
||||
handleAssignPresenter(msg)
|
||||
case msg: GetUsers =>
|
||||
handleGetUsers(msg)
|
||||
case msg: ChangeUserStatus =>
|
||||
handleChangeUserStatus(msg)
|
||||
case msg: EjectUserFromMeeting =>
|
||||
handleEjectUserFromMeeting(msg)
|
||||
case msg: UserEmojiStatus =>
|
||||
handleUserEmojiStatus(msg)
|
||||
case msg: UserShareWebcam =>
|
||||
handleUserShareWebcam(msg)
|
||||
case msg: UserUnshareWebcam =>
|
||||
handleUserunshareWebcam(msg)
|
||||
case msg: MuteMeetingRequest =>
|
||||
handleMuteMeetingRequest(msg)
|
||||
case msg: MuteAllExceptPresenterRequest =>
|
||||
handleMuteAllExceptPresenterRequest(msg)
|
||||
case msg: IsMeetingMutedRequest =>
|
||||
handleIsMeetingMutedRequest(msg)
|
||||
case msg: MuteUserRequest =>
|
||||
handleMuteUserRequest(msg)
|
||||
case msg: EjectUserFromVoiceRequest =>
|
||||
handleEjectUserRequest(msg)
|
||||
case msg: SetLockSettings =>
|
||||
handleSetLockSettings(msg)
|
||||
case msg: GetLockSettings =>
|
||||
handleGetLockSettings(msg)
|
||||
case msg: LockUserRequest =>
|
||||
handleLockUserRequest(msg)
|
||||
case msg: InitLockSettings =>
|
||||
handleInitLockSettings(msg)
|
||||
case msg: InitAudioSettings =>
|
||||
handleInitAudioSettings(msg)
|
||||
case msg: GetChatHistoryRequest =>
|
||||
handleGetChatHistoryRequest(msg)
|
||||
case msg: SendPublicMessageRequest =>
|
||||
handleSendPublicMessageRequest(msg)
|
||||
case msg: SendPrivateMessageRequest =>
|
||||
handleSendPrivateMessageRequest(msg)
|
||||
case msg: UserConnectedToGlobalAudio =>
|
||||
handleUserConnectedToGlobalAudio(msg)
|
||||
case msg: UserDisconnectedFromGlobalAudio =>
|
||||
handleUserDisconnectedFromGlobalAudio(msg)
|
||||
case msg: GetCurrentLayoutRequest =>
|
||||
handleGetCurrentLayoutRequest(msg)
|
||||
case msg: BroadcastLayoutRequest =>
|
||||
handleBroadcastLayoutRequest(msg)
|
||||
case msg: InitializeMeeting =>
|
||||
handleInitializeMeeting(msg)
|
||||
case msg: ClearPresentation =>
|
||||
handleClearPresentation(msg)
|
||||
case msg: PresentationConversionUpdate =>
|
||||
handlePresentationConversionUpdate(msg)
|
||||
case msg: PresentationPageCountError =>
|
||||
handlePresentationPageCountError(msg)
|
||||
case msg: PresentationSlideGenerated =>
|
||||
handlePresentationSlideGenerated(msg)
|
||||
case msg: PresentationConversionCompleted =>
|
||||
handlePresentationConversionCompleted(msg)
|
||||
case msg: RemovePresentation =>
|
||||
handleRemovePresentation(msg)
|
||||
case msg: GetPresentationInfo =>
|
||||
handleGetPresentationInfo(msg)
|
||||
case msg: SendCursorUpdate =>
|
||||
handleSendCursorUpdate(msg)
|
||||
case msg: ResizeAndMoveSlide =>
|
||||
handleResizeAndMoveSlide(msg)
|
||||
case msg: GotoSlide =>
|
||||
handleGotoSlide(msg)
|
||||
case msg: SharePresentation =>
|
||||
handleSharePresentation(msg)
|
||||
case msg: GetSlideInfo =>
|
||||
handleGetSlideInfo(msg)
|
||||
case msg: PreuploadedPresentations =>
|
||||
handlePreuploadedPresentations(msg)
|
||||
case msg: SendWhiteboardAnnotationRequest =>
|
||||
handleSendWhiteboardAnnotationRequest(msg)
|
||||
case msg: GetWhiteboardShapesRequest =>
|
||||
handleGetWhiteboardShapesRequest(msg)
|
||||
case msg: ClearWhiteboardRequest =>
|
||||
handleClearWhiteboardRequest(msg)
|
||||
case msg: UndoWhiteboardRequest =>
|
||||
handleUndoWhiteboardRequest(msg)
|
||||
case msg: EnableWhiteboardRequest =>
|
||||
handleEnableWhiteboardRequest(msg)
|
||||
case msg: IsWhiteboardEnabledRequest =>
|
||||
handleIsWhiteboardEnabledRequest(msg)
|
||||
case msg: SetRecordingStatus =>
|
||||
handleSetRecordingStatus(msg)
|
||||
case msg: GetRecordingStatus =>
|
||||
handleGetRecordingStatus(msg)
|
||||
case msg: StartCustomPollRequest =>
|
||||
handleStartCustomPollRequest(msg)
|
||||
case msg: StartPollRequest =>
|
||||
handleStartPollRequest(msg)
|
||||
case msg: StopPollRequest =>
|
||||
handleStopPollRequest(msg)
|
||||
case msg: ShowPollResultRequest =>
|
||||
handleShowPollResultRequest(msg)
|
||||
case msg: HidePollResultRequest =>
|
||||
handleHidePollResultRequest(msg)
|
||||
case msg: RespondToPollRequest =>
|
||||
handleRespondToPollRequest(msg)
|
||||
case msg: GetPollRequest =>
|
||||
handleGetPollRequest(msg)
|
||||
case msg: GetCurrentPollRequest =>
|
||||
handleGetCurrentPollRequest(msg)
|
||||
case msg: MonitorNumberOfUsers => liveMeeting.handleMonitorNumberOfWebUsers(msg)
|
||||
case msg: ValidateAuthToken => liveMeeting.handleValidateAuthToken(msg)
|
||||
case msg: RegisterUser => liveMeeting.handleRegisterUser(msg)
|
||||
case msg: UserJoinedVoiceConfMessage => liveMeeting.handleUserJoinedVoiceConfMessage(msg)
|
||||
case msg: UserLeftVoiceConfMessage => liveMeeting.handleUserLeftVoiceConfMessage(msg)
|
||||
case msg: UserMutedInVoiceConfMessage => liveMeeting.handleUserMutedInVoiceConfMessage(msg)
|
||||
case msg: UserTalkingInVoiceConfMessage => liveMeeting.handleUserTalkingInVoiceConfMessage(msg)
|
||||
case msg: VoiceConfRecordingStartedMessage => liveMeeting.handleVoiceConfRecordingStartedMessage(msg)
|
||||
case msg: UserJoining => liveMeeting.handleUserJoin(msg)
|
||||
case msg: UserLeaving => liveMeeting.handleUserLeft(msg)
|
||||
case msg: AssignPresenter => liveMeeting.handleAssignPresenter(msg)
|
||||
case msg: GetUsers => liveMeeting.handleGetUsers(msg)
|
||||
case msg: ChangeUserStatus => liveMeeting.handleChangeUserStatus(msg)
|
||||
case msg: EjectUserFromMeeting => liveMeeting.handleEjectUserFromMeeting(msg)
|
||||
case msg: UserEmojiStatus => liveMeeting.handleUserEmojiStatus(msg)
|
||||
case msg: UserShareWebcam => liveMeeting.handleUserShareWebcam(msg)
|
||||
case msg: UserUnshareWebcam => liveMeeting.handleUserunshareWebcam(msg)
|
||||
case msg: MuteMeetingRequest => liveMeeting.handleMuteMeetingRequest(msg)
|
||||
case msg: MuteAllExceptPresenterRequest => liveMeeting.handleMuteAllExceptPresenterRequest(msg)
|
||||
case msg: IsMeetingMutedRequest => liveMeeting.handleIsMeetingMutedRequest(msg)
|
||||
case msg: MuteUserRequest => liveMeeting.handleMuteUserRequest(msg)
|
||||
case msg: EjectUserFromVoiceRequest => liveMeeting.handleEjectUserRequest(msg)
|
||||
case msg: TransferUserToMeetingRequest => liveMeeting.handleTransferUserToMeeting(msg)
|
||||
case msg: SetLockSettings => liveMeeting.handleSetLockSettings(msg)
|
||||
case msg: GetLockSettings => liveMeeting.handleGetLockSettings(msg)
|
||||
case msg: LockUserRequest => liveMeeting.handleLockUserRequest(msg)
|
||||
case msg: InitLockSettings => liveMeeting.handleInitLockSettings(msg)
|
||||
case msg: InitAudioSettings => liveMeeting.handleInitAudioSettings(msg)
|
||||
case msg: GetChatHistoryRequest => liveMeeting.handleGetChatHistoryRequest(msg)
|
||||
case msg: SendPublicMessageRequest => liveMeeting.handleSendPublicMessageRequest(msg)
|
||||
case msg: SendPrivateMessageRequest => liveMeeting.handleSendPrivateMessageRequest(msg)
|
||||
case msg: UserConnectedToGlobalAudio => liveMeeting.handleUserConnectedToGlobalAudio(msg)
|
||||
case msg: UserDisconnectedFromGlobalAudio => liveMeeting.handleUserDisconnectedFromGlobalAudio(msg)
|
||||
case msg: GetCurrentLayoutRequest => liveMeeting.handleGetCurrentLayoutRequest(msg)
|
||||
case msg: BroadcastLayoutRequest => liveMeeting.handleBroadcastLayoutRequest(msg)
|
||||
case msg: InitializeMeeting => liveMeeting.handleInitializeMeeting(msg)
|
||||
case msg: ClearPresentation => liveMeeting.handleClearPresentation(msg)
|
||||
case msg: PresentationConversionUpdate => liveMeeting.handlePresentationConversionUpdate(msg)
|
||||
case msg: PresentationPageCountError => liveMeeting.handlePresentationPageCountError(msg)
|
||||
case msg: PresentationSlideGenerated => liveMeeting.handlePresentationSlideGenerated(msg)
|
||||
case msg: PresentationConversionCompleted => liveMeeting.handlePresentationConversionCompleted(msg)
|
||||
case msg: RemovePresentation => liveMeeting.handleRemovePresentation(msg)
|
||||
case msg: GetPresentationInfo => liveMeeting.handleGetPresentationInfo(msg)
|
||||
case msg: SendCursorUpdate => liveMeeting.handleSendCursorUpdate(msg)
|
||||
case msg: ResizeAndMoveSlide => liveMeeting.handleResizeAndMoveSlide(msg)
|
||||
case msg: GotoSlide => liveMeeting.handleGotoSlide(msg)
|
||||
case msg: SharePresentation => liveMeeting.handleSharePresentation(msg)
|
||||
case msg: GetSlideInfo => liveMeeting.handleGetSlideInfo(msg)
|
||||
case msg: PreuploadedPresentations => liveMeeting.handlePreuploadedPresentations(msg)
|
||||
case msg: SendWhiteboardAnnotationRequest => liveMeeting.handleSendWhiteboardAnnotationRequest(msg)
|
||||
case msg: GetWhiteboardShapesRequest => liveMeeting.handleGetWhiteboardShapesRequest(msg)
|
||||
case msg: ClearWhiteboardRequest => liveMeeting.handleClearWhiteboardRequest(msg)
|
||||
case msg: UndoWhiteboardRequest => liveMeeting.handleUndoWhiteboardRequest(msg)
|
||||
case msg: EnableWhiteboardRequest => liveMeeting.handleEnableWhiteboardRequest(msg)
|
||||
case msg: IsWhiteboardEnabledRequest => liveMeeting.handleIsWhiteboardEnabledRequest(msg)
|
||||
case msg: SetRecordingStatus => liveMeeting.handleSetRecordingStatus(msg)
|
||||
case msg: GetRecordingStatus => liveMeeting.handleGetRecordingStatus(msg)
|
||||
case msg: StartCustomPollRequest => liveMeeting.handleStartCustomPollRequest(msg)
|
||||
case msg: StartPollRequest => liveMeeting.handleStartPollRequest(msg)
|
||||
case msg: StopPollRequest => liveMeeting.handleStopPollRequest(msg)
|
||||
case msg: ShowPollResultRequest => liveMeeting.handleShowPollResultRequest(msg)
|
||||
case msg: HidePollResultRequest => liveMeeting.handleHidePollResultRequest(msg)
|
||||
case msg: RespondToPollRequest => liveMeeting.handleRespondToPollRequest(msg)
|
||||
case msg: GetPollRequest => liveMeeting.handleGetPollRequest(msg)
|
||||
case msg: GetCurrentPollRequest => liveMeeting.handleGetCurrentPollRequest(msg)
|
||||
// Breakout rooms
|
||||
case msg: BreakoutRoomsListMessage => liveMeeting.handleBreakoutRoomsList(msg)
|
||||
case msg: CreateBreakoutRooms => liveMeeting.handleCreateBreakoutRooms(msg)
|
||||
case msg: BreakoutRoomCreated => liveMeeting.handleBreakoutRoomCreated(msg)
|
||||
case msg: BreakoutRoomEnded => liveMeeting.handleBreakoutRoomEnded(msg)
|
||||
case msg: RequestBreakoutJoinURLInMessage => liveMeeting.handleRequestBreakoutJoinURL(msg)
|
||||
case msg: BreakoutRoomUsersUpdate => liveMeeting.handleBreakoutRoomUsersUpdate(msg)
|
||||
case msg: SendBreakoutUsersUpdate => liveMeeting.handleSendBreakoutUsersUpdate(msg)
|
||||
case msg: EndAllBreakoutRooms => liveMeeting.handleEndAllBreakoutRooms(msg)
|
||||
|
||||
case msg: ExtendMeetingDuration => liveMeeting.handleExtendMeetingDuration(msg)
|
||||
case msg: SendTimeRemainingUpdate => liveMeeting.handleSendTimeRemainingUpdate(msg)
|
||||
case msg: EndMeeting => liveMeeting.handleEndMeeting(msg)
|
||||
|
||||
// Closed Caption
|
||||
case msg: SendCaptionHistoryRequest => liveMeeting.handleSendCaptionHistoryRequest(msg)
|
||||
case msg: UpdateCaptionOwnerRequest => liveMeeting.handleUpdateCaptionOwnerRequest(msg)
|
||||
case msg: EditCaptionHistoryRequest => liveMeeting.handleEditCaptionHistoryRequest(msg)
|
||||
|
||||
case msg: EndMeeting => handleEndMeeting(msg)
|
||||
case StopMeetingActor => //exit
|
||||
case _ => // do nothing
|
||||
}
|
||||
|
||||
def hasMeetingEnded(): Boolean = {
|
||||
meetingModel.hasMeetingEnded()
|
||||
}
|
||||
|
||||
private def handleStartTimer() {
|
||||
// println("***************timer started******************")
|
||||
// val timerActor = new TimerActor(2000, self, "Hello")
|
||||
// timerActor.start
|
||||
}
|
||||
|
||||
private def handleHello() {
|
||||
// println("***************hello received on [" + System.currentTimeMillis() + "]******************")
|
||||
|
||||
// val timerActor = new TimerActor(2000, self, "Hello")
|
||||
// timerActor.start
|
||||
}
|
||||
|
||||
def webUserJoined() {
|
||||
if (usersModel.numWebUsers > 0) {
|
||||
meetingModel.resetLastWebUserLeftOn()
|
||||
}
|
||||
}
|
||||
|
||||
def startRecordingIfAutoStart() {
|
||||
if (mProps.recorded && !meetingModel.isRecording() && mProps.autoStartRecording && usersModel.numWebUsers == 1) {
|
||||
log.info("Auto start recording. meetingId={}", mProps.meetingID)
|
||||
meetingModel.recordingStarted()
|
||||
outGW.send(new RecordingStatusChanged(mProps.meetingID, mProps.recorded, "system", meetingModel.isRecording()))
|
||||
}
|
||||
}
|
||||
|
||||
def stopAutoStartedRecording() {
|
||||
if (mProps.recorded && meetingModel.isRecording() && mProps.autoStartRecording && usersModel.numWebUsers == 0) {
|
||||
log.info("Last web user left. Auto stopping recording. meetingId={}", mProps.meetingID)
|
||||
meetingModel.recordingStopped()
|
||||
outGW.send(new RecordingStatusChanged(mProps.meetingID, mProps.recorded, "system", meetingModel.isRecording()))
|
||||
}
|
||||
}
|
||||
|
||||
def startCheckingIfWeNeedToEndVoiceConf() {
|
||||
if (usersModel.numWebUsers == 0) {
|
||||
meetingModel.lastWebUserLeft()
|
||||
log.debug("MonitorNumberOfWebUsers started for meeting [" + mProps.meetingID + "]")
|
||||
}
|
||||
}
|
||||
|
||||
def handleMonitorNumberOfWebUsers() {
|
||||
// println("BACK TIMER")
|
||||
if (usersModel.numWebUsers == 0 && meetingModel.lastWebUserLeftOn > 0) {
|
||||
if (timeNowInMinutes - meetingModel.lastWebUserLeftOn > 2) {
|
||||
log.info("Empty meeting. Ejecting all users from voice. meetingId={}", mProps.meetingID)
|
||||
outGW.send(new EjectAllVoiceUsers(mProps.meetingID, mProps.recorded, mProps.voiceBridge))
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
val now = timeNowInMinutes
|
||||
|
||||
// println("(" + meetingModel.startedOn + "+" + mProps.duration + ") - " + now + " = " + ((meetingModel.startedOn + mProps.duration) - now) + " < 15")
|
||||
|
||||
if (mProps.duration > 0 && (((meetingModel.startedOn + mProps.duration) - now) < 15)) {
|
||||
// log.warning("MEETING WILL END IN 15 MINUTES!!!!")
|
||||
}
|
||||
}
|
||||
|
||||
def timeNowInMinutes(): Long = {
|
||||
TimeUnit.NANOSECONDS.toMinutes(System.nanoTime())
|
||||
}
|
||||
|
||||
def sendMeetingHasEnded(userId: String) {
|
||||
outGW.send(new MeetingHasEnded(mProps.meetingID, userId))
|
||||
outGW.send(new DisconnectUser(mProps.meetingID, userId))
|
||||
}
|
||||
|
||||
private def handleEndMeeting(msg: EndMeeting) {
|
||||
meetingModel.meetingHasEnded
|
||||
outGW.send(new MeetingEnded(msg.meetingID, mProps.recorded, mProps.voiceBridge))
|
||||
outGW.send(new DisconnectAllUsers(msg.meetingID))
|
||||
}
|
||||
|
||||
private def handleVoiceConfRecordingStartedMessage(msg: VoiceConfRecordingStartedMessage) {
|
||||
if (msg.recording) {
|
||||
meetingModel.setVoiceRecordingFilename(msg.recordStream)
|
||||
outGW.send(new VoiceRecordingStarted(mProps.meetingID, mProps.recorded, msg.recordStream, msg.timestamp, mProps.voiceBridge))
|
||||
} else {
|
||||
meetingModel.setVoiceRecordingFilename("")
|
||||
outGW.send(new VoiceRecordingStopped(mProps.meetingID, mProps.recorded, msg.recordStream, msg.timestamp, mProps.voiceBridge))
|
||||
}
|
||||
}
|
||||
|
||||
private def handleSetRecordingStatus(msg: SetRecordingStatus) {
|
||||
log.info("Change recording status. meetingId=" + mProps.meetingID + " recording=" + msg.recording)
|
||||
if (mProps.allowStartStopRecording && meetingModel.isRecording() != msg.recording) {
|
||||
if (msg.recording) {
|
||||
meetingModel.recordingStarted()
|
||||
} else {
|
||||
meetingModel.recordingStopped()
|
||||
}
|
||||
|
||||
outGW.send(new RecordingStatusChanged(mProps.meetingID, mProps.recorded, msg.userId, msg.recording))
|
||||
}
|
||||
}
|
||||
|
||||
private def handleGetRecordingStatus(msg: GetRecordingStatus) {
|
||||
outGW.send(new GetRecordingStatusReply(mProps.meetingID, mProps.recorded, msg.userId, meetingModel.isRecording().booleanValue()))
|
||||
}
|
||||
|
||||
def lockLayout(lock: Boolean) {
|
||||
meetingModel.lockLayout(lock)
|
||||
}
|
||||
|
||||
def newPermissions(np: Permissions) {
|
||||
meetingModel.setPermissions(np)
|
||||
}
|
||||
|
||||
def permissionsEqual(other: Permissions): Boolean = {
|
||||
meetingModel.permissionsEqual(other)
|
||||
}
|
||||
|
||||
// Broadcast video stream,
|
||||
private def handleDeskShareStartedRequest(msg: DeskShareStartedRequest) {
|
||||
log.info("handleDeskShareStartedRequest: dsStarted=" + meetingModel.getDeskShareStarted())
|
||||
|
@ -5,8 +5,13 @@ import java.util.concurrent.TimeUnit
|
||||
|
||||
case object StopMeetingActor
|
||||
case class MeetingProperties(meetingID: String, externalMeetingID: String, meetingName: String, recorded: Boolean,
|
||||
voiceBridge: String, duration: Long, autoStartRecording: Boolean, allowStartStopRecording: Boolean,
|
||||
moderatorPass: String, viewerPass: String, createTime: Long, createDate: String, red5DeskShareIP: String, red5DeskShareApp: String)
|
||||
voiceBridge: String, duration: Int, autoStartRecording: Boolean, allowStartStopRecording: Boolean,
|
||||
moderatorPass: String, viewerPass: String, createTime: Long, createDate: String,
|
||||
red5DeskShareIP: String, red5DeskShareApp: String, isBreakout: Boolean)
|
||||
|
||||
case class MeetingExtensionProp(maxExtensions: Int = 2, numExtensions: Int = 0, extendByMinutes: Int = 20,
|
||||
sendNotice: Boolean = true, sent15MinNotice: Boolean = false,
|
||||
sent10MinNotice: Boolean = false, sent5MinNotice: Boolean = false)
|
||||
|
||||
class MeetingModel {
|
||||
private var audioSettingsInited = false
|
||||
@ -18,7 +23,6 @@ class MeetingModel {
|
||||
private var meetingEnded = false
|
||||
private var meetingMuted = false
|
||||
|
||||
val TIMER_INTERVAL = 30000
|
||||
private var hasLastWebUserLeft = false
|
||||
private var lastWebUserLeftOnTimestamp: Long = 0
|
||||
|
||||
@ -28,7 +32,12 @@ class MeetingModel {
|
||||
private var desktopShareVideoWidth = 0
|
||||
private var desktopShareVideoHeight = 0
|
||||
|
||||
val startedOn = timeNowInMinutes;
|
||||
private var extension = new MeetingExtensionProp
|
||||
|
||||
val startedOn = timeNowInSeconds;
|
||||
|
||||
var breakoutRoomsStartedOn: Long = 0;
|
||||
var breakoutRoomsdurationInMinutes: Int = 0;
|
||||
|
||||
def resetDesktopSharingParams() = {
|
||||
broadcastingRTMP = false
|
||||
@ -63,30 +72,6 @@ class MeetingModel {
|
||||
desktopShareVideoHeight
|
||||
}
|
||||
|
||||
def muteMeeting() {
|
||||
meetingMuted = true
|
||||
}
|
||||
|
||||
def unmuteMeeting() {
|
||||
meetingMuted = false
|
||||
}
|
||||
|
||||
def isMeetingMuted(): Boolean = {
|
||||
meetingMuted
|
||||
}
|
||||
|
||||
def recordingStarted() {
|
||||
recording = true
|
||||
}
|
||||
|
||||
def recordingStopped() {
|
||||
recording = false
|
||||
}
|
||||
|
||||
def isRecording(): Boolean = {
|
||||
recording
|
||||
}
|
||||
|
||||
def broadcastingRTMPStarted() {
|
||||
broadcastingRTMP = true
|
||||
}
|
||||
@ -99,26 +84,6 @@ class MeetingModel {
|
||||
broadcastingRTMP = false
|
||||
}
|
||||
|
||||
def lastWebUserLeft() {
|
||||
lastWebUserLeftOnTimestamp = timeNowInMinutes
|
||||
}
|
||||
|
||||
def lastWebUserLeftOn(): Long = {
|
||||
lastWebUserLeftOnTimestamp
|
||||
}
|
||||
|
||||
def resetLastWebUserLeftOn() {
|
||||
lastWebUserLeftOnTimestamp = 0
|
||||
}
|
||||
|
||||
def setVoiceRecordingFilename(path: String) {
|
||||
voiceRecordingFilename = path
|
||||
}
|
||||
|
||||
def getVoiceRecordingFilename(): String = {
|
||||
voiceRecordingFilename
|
||||
}
|
||||
|
||||
def setRTMPBroadcastingUrl(path: String) {
|
||||
println("---RTMP broadcastUrl changed to:" + path)
|
||||
rtmpBroadcastingUrl = path
|
||||
@ -128,47 +93,40 @@ class MeetingModel {
|
||||
rtmpBroadcastingUrl
|
||||
}
|
||||
|
||||
def permisionsInitialized(): Boolean = {
|
||||
permissionsInited
|
||||
def isExtensionAllowed(): Boolean = extension.numExtensions < extension.maxExtensions
|
||||
def incNumExtension(): Int = {
|
||||
if (extension.numExtensions < extension.maxExtensions) {
|
||||
extension = extension.copy(numExtensions = extension.numExtensions + 1); extension.numExtensions
|
||||
}
|
||||
extension.numExtensions
|
||||
}
|
||||
|
||||
def initializePermissions() {
|
||||
permissionsInited = true
|
||||
}
|
||||
def notice15MinutesSent() = extension = extension.copy(sent15MinNotice = true)
|
||||
def notice10MinutesSent() = extension = extension.copy(sent10MinNotice = true)
|
||||
def notice5MinutesSent() = extension = extension.copy(sent5MinNotice = true)
|
||||
|
||||
def audioSettingsInitialized(): Boolean = {
|
||||
audioSettingsInited
|
||||
}
|
||||
|
||||
def initializeAudioSettings() {
|
||||
audioSettingsInited = true
|
||||
}
|
||||
|
||||
def permissionsEqual(other: Permissions): Boolean = {
|
||||
permissions == other
|
||||
}
|
||||
|
||||
def lockLayout(lock: Boolean) {
|
||||
permissions = permissions.copy(lockedLayout = lock)
|
||||
}
|
||||
|
||||
def getPermissions(): Permissions = {
|
||||
permissions
|
||||
}
|
||||
|
||||
def setPermissions(p: Permissions) {
|
||||
permissions = p
|
||||
}
|
||||
|
||||
def meetingHasEnded() {
|
||||
meetingEnded = true
|
||||
}
|
||||
|
||||
def hasMeetingEnded(): Boolean = {
|
||||
meetingEnded
|
||||
}
|
||||
|
||||
def timeNowInMinutes(): Long = {
|
||||
TimeUnit.NANOSECONDS.toMinutes(System.nanoTime())
|
||||
}
|
||||
}
|
||||
def getMeetingExtensionProp(): MeetingExtensionProp = extension
|
||||
def muteMeeting() = meetingMuted = true
|
||||
def unmuteMeeting() = meetingMuted = false
|
||||
def isMeetingMuted(): Boolean = meetingMuted
|
||||
def recordingStarted() = recording = true
|
||||
def recordingStopped() = recording = false
|
||||
def isRecording(): Boolean = recording
|
||||
def lastWebUserLeft() = lastWebUserLeftOnTimestamp = timeNowInMinutes
|
||||
def lastWebUserLeftOn(): Long = lastWebUserLeftOnTimestamp
|
||||
def resetLastWebUserLeftOn() = lastWebUserLeftOnTimestamp = 0
|
||||
def setVoiceRecordingFilename(path: String) = voiceRecordingFilename = path
|
||||
def getVoiceRecordingFilename(): String = voiceRecordingFilename
|
||||
def permisionsInitialized(): Boolean = permissionsInited
|
||||
def initializePermissions() = permissionsInited = true
|
||||
def audioSettingsInitialized(): Boolean = audioSettingsInited
|
||||
def initializeAudioSettings() = audioSettingsInited = true
|
||||
def permissionsEqual(other: Permissions): Boolean = permissions == other
|
||||
def lockLayout(lock: Boolean) = permissions = permissions.copy(lockedLayout = lock)
|
||||
def getPermissions(): Permissions = permissions
|
||||
def setPermissions(p: Permissions) = permissions = p
|
||||
def meetingHasEnded() = meetingEnded = true
|
||||
def hasMeetingEnded(): Boolean = meetingEnded
|
||||
def timeNowInMinutes(): Long = TimeUnit.NANOSECONDS.toMinutes(System.nanoTime())
|
||||
def timeNowInSeconds(): Long = TimeUnit.NANOSECONDS.toSeconds(System.nanoTime())
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ import org.bigbluebutton.common.messages.StartRecordingVoiceConfRequestMessage
|
||||
import org.bigbluebutton.common.messages.StopRecordingVoiceConfRequestMessage
|
||||
import org.bigbluebutton.core.pubsub.senders.MeetingMessageToJsonConverter
|
||||
import org.bigbluebutton.core.pubsub.senders.PesentationMessageToJsonConverter
|
||||
import org.bigbluebutton.core.pubsub.senders.CaptionMessageToJsonConverter
|
||||
import org.bigbluebutton.core.pubsub.senders.DeskShareMessageToJsonConverter
|
||||
import org.bigbluebutton.common.messages.GetPresentationInfoReplyMessage
|
||||
import org.bigbluebutton.common.messages.PresentationRemovedMessage
|
||||
@ -29,13 +30,14 @@ import org.bigbluebutton.common.messages.UserEjectedFromMeetingMessage
|
||||
import org.bigbluebutton.common.messages.LockLayoutMessage
|
||||
import org.bigbluebutton.core.pubsub.senders.WhiteboardMessageToJsonConverter
|
||||
import org.bigbluebutton.common.converters.ToJsonEncoder
|
||||
import org.bigbluebutton.common.messages.TransferUserToVoiceConfRequestMessage
|
||||
|
||||
object MessageSenderActor {
|
||||
def props(meetingId: String, msgSender: MessageSender): Props =
|
||||
Props(classOf[MessageSenderActor], meetingId, msgSender)
|
||||
def props(msgSender: MessageSender): Props =
|
||||
Props(classOf[MessageSenderActor], msgSender)
|
||||
}
|
||||
|
||||
class MessageSenderActor(val meetingId: String, val service: MessageSender)
|
||||
class MessageSenderActor(val service: MessageSender)
|
||||
extends Actor with ActorLogging {
|
||||
|
||||
val encoder = new ToJsonEncoder()
|
||||
@ -101,6 +103,7 @@ class MessageSenderActor(val meetingId: String, val service: MessageSender)
|
||||
case msg: UserVoiceTalking => handleUserVoiceTalking(msg)
|
||||
case msg: MuteVoiceUser => handleMuteVoiceUser(msg)
|
||||
case msg: EjectVoiceUser => handleEjectVoiceUser(msg)
|
||||
case msg: TransferUserToMeeting => handleTransferUserToMeeting(msg)
|
||||
case msg: GetUsersInVoiceConference => handleGetUsersFromVoiceConference(msg)
|
||||
case msg: UserJoinedVoice => handleUserJoinedVoice(msg)
|
||||
case msg: UserLeftVoice => handleUserLeftVoice(msg)
|
||||
@ -115,6 +118,18 @@ class MessageSenderActor(val meetingId: String, val service: MessageSender)
|
||||
case msg: UndoWhiteboardEvent => handleUndoWhiteboardEvent(msg)
|
||||
case msg: WhiteboardEnabledEvent => handleWhiteboardEnabledEvent(msg)
|
||||
case msg: IsWhiteboardEnabledReply => handleIsWhiteboardEnabledReply(msg)
|
||||
// breakout room cases
|
||||
case msg: BreakoutRoomsListOutMessage => handleBreakoutRoomsListOutMessage(msg)
|
||||
case msg: BreakoutRoomStartedOutMessage => handleBreakoutRoomStartedOutMessage(msg)
|
||||
case msg: BreakoutRoomEndedOutMessage => handleBreakoutRoomEndedOutMessage(msg)
|
||||
case msg: BreakoutRoomJoinURLOutMessage => handleBreakoutRoomJoinURLOutMessage(msg)
|
||||
case msg: UpdateBreakoutUsersOutMessage => handleUpdateBreakoutUsersOutMessage(msg)
|
||||
case msg: MeetingTimeRemainingUpdate => handleMeetingTimeRemainingUpdate(msg)
|
||||
case msg: BreakoutRoomsTimeRemainingUpdateOutMessage => handleBreakoutRoomsTimeRemainingUpdate(msg)
|
||||
|
||||
case msg: SendCaptionHistoryReply => handleSendCaptionHistoryReply(msg)
|
||||
case msg: UpdateCaptionOwnerReply => handleUpdateCaptionOwnerReply(msg)
|
||||
case msg: EditCaptionHistoryReply => handleEditCaptionHistoryReply(msg)
|
||||
case msg: DeskShareStartRTMPBroadcast => handleDeskShareStartRTMPBroadcast(msg)
|
||||
case msg: DeskShareStopRTMPBroadcast => handleDeskShareStopRTMPBroadcast(msg)
|
||||
case msg: DeskShareNotifyViewersRTMP => handleDeskShareNotifyViewersRTMP(msg)
|
||||
@ -599,7 +614,11 @@ class MessageSenderActor(val meetingId: String, val service: MessageSender)
|
||||
private def handleEjectVoiceUser(msg: EjectVoiceUser) {
|
||||
val m = new EjectUserFromVoiceConfRequestMessage(msg.meetingID, msg.voiceConfId, msg.voiceUserId)
|
||||
service.send(MessagingConstants.TO_VOICE_CONF_SYSTEM_CHAN, m.toJson())
|
||||
}
|
||||
|
||||
private def handleTransferUserToMeeting(msg: TransferUserToMeeting) {
|
||||
val m = new TransferUserToVoiceConfRequestMessage(msg.voiceConfId, msg.targetVoiceConfId, msg.userId);
|
||||
service.send(MessagingConstants.TO_VOICE_CONF_SYSTEM_CHAN, m.toJson())
|
||||
}
|
||||
|
||||
private def handleUserLeftVoice(msg: UserLeftVoice) {
|
||||
@ -613,12 +632,14 @@ class MessageSenderActor(val meetingId: String, val service: MessageSender)
|
||||
}
|
||||
|
||||
private def handleValidateAuthTokenReply(msg: ValidateAuthTokenReply) {
|
||||
println("**** handleValidateAuthTokenReply *****")
|
||||
val json = UsersMessageToJsonConverter.validateAuthTokenReplyToJson(msg)
|
||||
//println("************** Publishing [" + json + "] *******************")
|
||||
service.send(MessagingConstants.FROM_USERS_CHANNEL, json)
|
||||
}
|
||||
|
||||
private def handleValidateAuthTokenTimedOut(msg: ValidateAuthTokenTimedOut) {
|
||||
println("**** handleValidateAuthTokenTimedOut *****")
|
||||
val json = UsersMessageToJsonConverter.validateAuthTokenTimeoutToJson(msg)
|
||||
//println("************** Publishing [" + json + "] *******************")
|
||||
service.send(MessagingConstants.FROM_USERS_CHANNEL, json)
|
||||
@ -683,4 +704,56 @@ class MessageSenderActor(val meetingId: String, val service: MessageSender)
|
||||
val json = WhiteboardMessageToJsonConverter.isWhiteboardEnabledReplyToJson(msg)
|
||||
service.send(MessagingConstants.FROM_WHITEBOARD_CHANNEL, json)
|
||||
}
|
||||
|
||||
private def handleBreakoutRoomsListOutMessage(msg: BreakoutRoomsListOutMessage) {
|
||||
val json = MeetingMessageToJsonConverter.breakoutRoomsListOutMessageToJson(msg)
|
||||
service.send(MessagingConstants.FROM_USERS_CHANNEL, json)
|
||||
}
|
||||
|
||||
private def handleBreakoutRoomStartedOutMessage(msg: BreakoutRoomStartedOutMessage) {
|
||||
val json = MeetingMessageToJsonConverter.breakoutRoomStartedOutMessageToJson(msg)
|
||||
service.send(MessagingConstants.FROM_USERS_CHANNEL, json)
|
||||
}
|
||||
|
||||
private def handleBreakoutRoomEndedOutMessage(msg: BreakoutRoomEndedOutMessage) {
|
||||
val json = MeetingMessageToJsonConverter.breakoutRoomEndedOutMessageToJson(msg)
|
||||
service.send(MessagingConstants.FROM_USERS_CHANNEL, json)
|
||||
}
|
||||
|
||||
private def handleBreakoutRoomJoinURLOutMessage(msg: BreakoutRoomJoinURLOutMessage) {
|
||||
val json = MeetingMessageToJsonConverter.breakoutRoomJoinURLOutMessageToJson(msg)
|
||||
service.send(MessagingConstants.FROM_USERS_CHANNEL, json)
|
||||
}
|
||||
|
||||
private def handleUpdateBreakoutUsersOutMessage(msg: UpdateBreakoutUsersOutMessage) {
|
||||
val json = MeetingMessageToJsonConverter.updateBreakoutUsersOutMessageToJson(msg)
|
||||
service.send(MessagingConstants.FROM_USERS_CHANNEL, json)
|
||||
}
|
||||
|
||||
private def handleMeetingTimeRemainingUpdate(msg: MeetingTimeRemainingUpdate) {
|
||||
val json = MeetingMessageToJsonConverter.meetingTimeRemainingUpdateToJson(msg)
|
||||
service.send(MessagingConstants.FROM_USERS_CHANNEL, json)
|
||||
}
|
||||
|
||||
private def handleSendCaptionHistoryReply(msg: SendCaptionHistoryReply) {
|
||||
val json = CaptionMessageToJsonConverter.sendCaptionHistoryReplyToJson(msg)
|
||||
service.send(MessagingConstants.FROM_CAPTION_CHANNEL, json)
|
||||
}
|
||||
|
||||
private def handleUpdateCaptionOwnerReply(msg: UpdateCaptionOwnerReply) {
|
||||
val json = CaptionMessageToJsonConverter.updateCaptionOwnerReplyToJson(msg)
|
||||
service.send(MessagingConstants.FROM_CAPTION_CHANNEL, json)
|
||||
}
|
||||
|
||||
private def handleEditCaptionHistoryReply(msg: EditCaptionHistoryReply) {
|
||||
println("handleEditCaptionHistoryReply")
|
||||
val json = CaptionMessageToJsonConverter.editCaptionHistoryReplyToJson(msg)
|
||||
println(json)
|
||||
service.send(MessagingConstants.FROM_CAPTION_CHANNEL, json)
|
||||
}
|
||||
|
||||
private def handleBreakoutRoomsTimeRemainingUpdate(msg: BreakoutRoomsTimeRemainingUpdateOutMessage) {
|
||||
val json = MeetingMessageToJsonConverter.breakoutRoomsTimeRemainingUpdateToJson(msg)
|
||||
service.send(MessagingConstants.FROM_USERS_CHANNEL, json)
|
||||
}
|
||||
}
|
||||
|
@ -2,20 +2,18 @@ package org.bigbluebutton.core
|
||||
|
||||
import akka.actor.ActorRef
|
||||
import akka.actor.ActorContext
|
||||
import org.bigbluebutton.core.api.MessageOutGateway
|
||||
import org.bigbluebutton.core.service.recorder.RecorderApplication
|
||||
import org.bigbluebutton.core.bus.OutgoingEventBus
|
||||
import org.bigbluebutton.core.bus.BigBlueButtonOutMessage
|
||||
import org.bigbluebutton.core.api.IOutMessage
|
||||
|
||||
object OutMessageGateway {
|
||||
def apply(meetingId: String, recorder: RecorderApplication, sender: MessageSender)(implicit context: ActorContext) =
|
||||
new OutMessageGateway(meetingId, recorder, sender)(context)
|
||||
def apply(outgoingEventBus: OutgoingEventBus) =
|
||||
new OutMessageGateway(outgoingEventBus)
|
||||
}
|
||||
|
||||
class OutMessageGateway(val meetingId: String, val recorder: RecorderApplication, val sender: MessageSender)(implicit val context: ActorContext) {
|
||||
|
||||
private val outGW = context.actorOf(OutMessageGatewayActor.props(meetingId, recorder, sender), meetingId)
|
||||
class OutMessageGateway(outgoingEventBus: OutgoingEventBus) {
|
||||
|
||||
def send(msg: IOutMessage) {
|
||||
outGW forward msg
|
||||
outgoingEventBus.publish(BigBlueButtonOutMessage("outgoingMessageChannel", msg))
|
||||
}
|
||||
}
|
@ -8,8 +8,8 @@ import org.bigbluebutton.core.api._
|
||||
import java.util.concurrent.TimeUnit
|
||||
import org.bigbluebutton.core.util._
|
||||
import scala.concurrent.duration._
|
||||
import org.bigbluebutton.core.apps.{ PollApp, UsersApp, PresentationApp, LayoutApp, ChatApp, WhiteboardApp }
|
||||
import org.bigbluebutton.core.apps.{ ChatModel, LayoutModel, UsersModel, PollModel, WhiteboardModel }
|
||||
import org.bigbluebutton.core.apps.{ PollApp, UsersApp, PresentationApp, LayoutApp, ChatApp, WhiteboardApp, CaptionApp }
|
||||
import org.bigbluebutton.core.apps.{ ChatModel, LayoutModel, UsersModel, PollModel, WhiteboardModel, CaptionModel }
|
||||
import org.bigbluebutton.core.apps.PresentationModel
|
||||
import org.bigbluebutton.core.service.recorder.RecorderApplication
|
||||
|
||||
@ -21,8 +21,8 @@ object OutMessageGatewayActor {
|
||||
class OutMessageGatewayActor(val meetingId: String, val recorder: RecorderApplication, val msgSender: MessageSender)
|
||||
extends Actor with ActorLogging {
|
||||
|
||||
private val recorderActor = context.actorOf(RecorderActor.props(meetingId, recorder), "recorderActor-" + meetingId)
|
||||
private val msgSenderActor = context.actorOf(MessageSenderActor.props(meetingId, msgSender), "senderActor-" + meetingId)
|
||||
private val recorderActor = context.actorOf(RecorderActor.props(recorder), "recorderActor-" + meetingId)
|
||||
private val msgSenderActor = context.actorOf(MessageSenderActor.props(msgSender), "senderActor-" + meetingId)
|
||||
|
||||
def receive = {
|
||||
case msg: IOutMessage => {
|
||||
|
40
akka-bbb-apps/src/main/scala/org/bigbluebutton/core/Protocol.scala
Executable file
40
akka-bbb-apps/src/main/scala/org/bigbluebutton/core/Protocol.scala
Executable file
@ -0,0 +1,40 @@
|
||||
package org.bigbluebutton.core
|
||||
|
||||
import spray.json.{ DefaultJsonProtocol, JsValue, JsString, DeserializationException, JsonFormat }
|
||||
import org.bigbluebutton.core.api._
|
||||
|
||||
object UserMessagesProtocol extends DefaultJsonProtocol {
|
||||
/*
|
||||
implicit object RoleJsonFormat extends JsonFormat[Role.RoleType] {
|
||||
def write(obj: Role.RoleType): JsValue = JsString(obj.toString)
|
||||
|
||||
def read(json: JsValue): Role.RoleType = json match {
|
||||
case JsString(str) => Role.withName(str)
|
||||
case _ => throw new DeserializationException("Enum string expected")
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
implicit object MessageTypeFormat extends JsonFormat[MessageType.MessageType] {
|
||||
def write(obj: MessageType.MessageType): JsValue = JsString(obj.toString)
|
||||
|
||||
def read(json: JsValue): MessageType.MessageType = json match {
|
||||
case JsString(str) => MessageType.withName(str)
|
||||
case _ => throw new DeserializationException("Enum string expected")
|
||||
}
|
||||
}
|
||||
|
||||
implicit val breakoutRoomInPayloadFormat = jsonFormat2(BreakoutRoomInPayload)
|
||||
implicit val createBreakoutRoomsFormat = jsonFormat3(CreateBreakoutRooms)
|
||||
implicit val breakoutRoomsListMessageFormat = jsonFormat1(BreakoutRoomsListMessage)
|
||||
implicit val requestBreakoutJoinURLInMessageFormat = jsonFormat3(RequestBreakoutJoinURLInMessage)
|
||||
implicit val transferUserToMeetingRequestFormat = jsonFormat3(TransferUserToMeetingRequest)
|
||||
implicit val endBreakoutRoomsFormat = jsonFormat1(EndAllBreakoutRooms)
|
||||
implicit val inMsgHeaderFormat = jsonFormat1(InMessageHeader)
|
||||
implicit val outMsgHeaderFormat = jsonFormat1(OutMsgHeader)
|
||||
implicit val outMsgEnvelopeHeaderFormat = jsonFormat2(OutMsgEnvelopeHeader)
|
||||
implicit val createBreakoutRoomOutMsgPayloadFormat = jsonFormat8(CreateBreakoutRoomOutMsgPayload)
|
||||
implicit val createBreakoutRoomOutMsgEnvelopePayloadFormat = jsonFormat2(CreateBreakoutRoomOutMsgEnvelopePayload)
|
||||
implicit val createBreakoutRoomOutMsgEnvelopeFormat = jsonFormat2(CreateBreakoutRoomOutMsgEnvelope)
|
||||
|
||||
}
|
16
akka-bbb-apps/src/main/scala/org/bigbluebutton/core/ProtocolMessages.scala
Executable file
16
akka-bbb-apps/src/main/scala/org/bigbluebutton/core/ProtocolMessages.scala
Executable file
@ -0,0 +1,16 @@
|
||||
package org.bigbluebutton.core
|
||||
|
||||
case class OutMsgHeader(name: String)
|
||||
case class OutMsgEnvelopeHeader(`type`: MessageType.MessageType, address: String)
|
||||
|
||||
trait OutMessage
|
||||
|
||||
case class CreateBreakoutRoomOutMsgEnvelope(header: OutMsgEnvelopeHeader,
|
||||
payload: CreateBreakoutRoomOutMsgEnvelopePayload)
|
||||
case class CreateBreakoutRoomOutMsgEnvelopePayload(header: OutMsgHeader,
|
||||
payload: CreateBreakoutRoomOutMsgPayload)
|
||||
case class CreateBreakoutRoomOutMsgPayload(breakoutId: String, name: String, parentId: String,
|
||||
voiceConfId: String, durationInMinutes: Int,
|
||||
moderatorPassword: String, viewerPassword: String,
|
||||
defaultPresentationUrl: String)
|
||||
|
@ -35,14 +35,15 @@ import org.bigbluebutton.core.recorders.events.DeskShareNotifyViewersRTMPRecordE
|
||||
// import org.bigbluebutton.core.service.whiteboard.WhiteboardKeyUtil
|
||||
import org.bigbluebutton.common.messages.WhiteboardKeyUtil
|
||||
import org.bigbluebutton.core.recorders.events.ModifyTextWhiteboardRecordEvent
|
||||
import org.bigbluebutton.core.recorders.events.EditCaptionHistoryRecordEvent
|
||||
import scala.collection.immutable.StringOps
|
||||
|
||||
object RecorderActor {
|
||||
def props(meetingId: String, recorder: RecorderApplication): Props =
|
||||
Props(classOf[RecorderActor], meetingId, recorder)
|
||||
def props(recorder: RecorderApplication): Props =
|
||||
Props(classOf[RecorderActor], recorder)
|
||||
}
|
||||
|
||||
class RecorderActor(val meetingId: String, val recorder: RecorderApplication)
|
||||
class RecorderActor(val recorder: RecorderApplication)
|
||||
extends Actor with ActorLogging {
|
||||
|
||||
def receive = {
|
||||
@ -71,6 +72,7 @@ class RecorderActor(val meetingId: String, val recorder: RecorderApplication)
|
||||
case msg: SendWhiteboardAnnotationEvent => handleSendWhiteboardAnnotationEvent(msg)
|
||||
case msg: ClearWhiteboardEvent => handleClearWhiteboardEvent(msg)
|
||||
case msg: UndoWhiteboardEvent => handleUndoWhiteboardEvent(msg)
|
||||
case msg: EditCaptionHistoryReply => handleEditCaptionHistoryReply(msg)
|
||||
case msg: DeskShareStartRTMPBroadcast => handleDeskShareStartRTMPBroadcast(msg)
|
||||
case msg: DeskShareStopRTMPBroadcast => handleDeskShareStopRTMPBroadcast(msg)
|
||||
case msg: DeskShareNotifyViewersRTMP => handleDeskShareNotifyViewersRTMP(msg)
|
||||
@ -442,6 +444,19 @@ class RecorderActor(val meetingId: String, val recorder: RecorderApplication)
|
||||
|
||||
}
|
||||
|
||||
private def handleEditCaptionHistoryReply(msg: EditCaptionHistoryReply) {
|
||||
if (msg.recorded) {
|
||||
val ev = new EditCaptionHistoryRecordEvent();
|
||||
ev.setTimestamp(TimestampGenerator.generateTimestamp);
|
||||
ev.setMeetingId(msg.meetingID);
|
||||
ev.setStartIndex(msg.startIndex.toString());
|
||||
ev.setEndIndex(msg.endIndex.toString());
|
||||
ev.setLocale(msg.locale);
|
||||
ev.setText(msg.text);
|
||||
recorder.record(msg.meetingID, ev);
|
||||
}
|
||||
}
|
||||
|
||||
private def handleDeskShareStartRTMPBroadcast(msg: DeskShareStartRTMPBroadcast) {
|
||||
val event = new DeskShareStartRTMPRecordEvent()
|
||||
event.setMeetingId(msg.conferenceName)
|
||||
@ -470,5 +485,4 @@ class RecorderActor(val meetingId: String, val recorder: RecorderApplication)
|
||||
log.info("handleDeskShareNotifyViewersRTMP " + msg.meetingID)
|
||||
recorder.record(msg.meetingID, event)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -3,13 +3,17 @@ package org.bigbluebutton.core
|
||||
import akka.actor.ActorRef
|
||||
import akka.actor.ActorContext
|
||||
import org.bigbluebutton.core.api.MessageOutGateway
|
||||
import org.bigbluebutton.core.bus._
|
||||
|
||||
object RunningMeeting {
|
||||
def apply(mProps: MeetingProperties, outGW: OutMessageGateway)(implicit context: ActorContext) =
|
||||
new RunningMeeting(mProps, outGW)(context)
|
||||
def apply(mProps: MeetingProperties, outGW: OutMessageGateway,
|
||||
eventBus: IncomingEventBus)(implicit context: ActorContext) =
|
||||
new RunningMeeting(mProps, outGW, eventBus)(context)
|
||||
}
|
||||
|
||||
class RunningMeeting(val mProps: MeetingProperties, val outGW: OutMessageGateway)(implicit val context: ActorContext) {
|
||||
class RunningMeeting(val mProps: MeetingProperties, val outGW: OutMessageGateway,
|
||||
val eventBus: IncomingEventBus)(implicit val context: ActorContext) {
|
||||
|
||||
val actorRef = context.actorOf(MeetingActor.props(mProps, eventBus, outGW), mProps.meetingID)
|
||||
|
||||
val actorRef = context.actorOf(MeetingActor.props(mProps, outGW), mProps.meetingID)
|
||||
}
|
@ -4,27 +4,77 @@ import org.bigbluebutton.core.api.Role._
|
||||
import org.bigbluebutton.core.apps.AnnotationVO
|
||||
import org.bigbluebutton.core.apps.Presentation
|
||||
import org.bigbluebutton.core.MeetingProperties
|
||||
import org.bigbluebutton.core.apps.BreakoutUser
|
||||
import spray.json.JsObject
|
||||
|
||||
trait InMessage { val meetingID: String }
|
||||
case class InMessageHeader(name: String)
|
||||
case class InHeaderAndJsonPayload(header: InMessageHeader, payload: JsObject)
|
||||
case class MessageProcessException(message: String) extends Exception(message)
|
||||
|
||||
case class PubSubPing(system: String, timestamp: Long)
|
||||
trait InMessage
|
||||
|
||||
case class IsMeetingActorAliveMessage(meetingId: String)
|
||||
case class KeepAliveMessage(aliveID: String)
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// System
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
case class PubSubPing(system: String, timestamp: Long) extends InMessage
|
||||
case class IsMeetingActorAliveMessage(meetingId: String) extends InMessage
|
||||
case class KeepAliveMessage(aliveID: String) extends InMessage
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Meeting
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
case class MonitorNumberOfUsers(meetingID: String) extends InMessage
|
||||
case class SendTimeRemainingUpdate(meetingId: String) extends InMessage
|
||||
case class ExtendMeetingDuration(meetingId: String, userId: String) extends InMessage
|
||||
case class CreateMeeting(meetingID: String, mProps: MeetingProperties) extends InMessage
|
||||
case class InitializeMeeting(meetingID: String, recorded: Boolean) extends InMessage
|
||||
case class DestroyMeeting(meetingID: String) extends InMessage
|
||||
case class StartMeeting(meetingID: String) extends InMessage
|
||||
case class EndMeeting(meetingID: String) extends InMessage
|
||||
case class EndMeeting(meetingId: String) extends InMessage
|
||||
case class LockSetting(meetingID: String, locked: Boolean, settings: Map[String, Boolean]) extends InMessage
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
// Breakout room
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Sent by user to request the breakout rooms list of a room
|
||||
case class BreakoutRoomsListMessage(meetingId: String) extends InMessage
|
||||
// Sent by user to request creation of breakout rooms
|
||||
case class CreateBreakoutRooms(meetingId: String, durationInMinutes: Int,
|
||||
rooms: Vector[BreakoutRoomInPayload]) extends InMessage
|
||||
case class BreakoutRoomInPayload(name: String, users: Vector[String])
|
||||
// Sent by user to request for a join URL in order to be able to join a breakout room
|
||||
case class RequestBreakoutJoinURLInMessage(meetingId: String, breakoutId: String,
|
||||
userId: String) extends InMessage
|
||||
// Sent by breakout actor to tell meeting actor that breakout room has been created.
|
||||
case class BreakoutRoomCreated(meetingId: String, breakoutRoomId: String) extends InMessage
|
||||
// Sent by breakout actor to tell meeting actor the list of users in the breakout room.
|
||||
case class BreakoutRoomUsersUpdate(meetingId: String, breakoutId: String,
|
||||
users: Vector[BreakoutUser]) extends InMessage
|
||||
// Send by internal actor to tell the breakout actor to send it's list of users to the main meeting actor.
|
||||
case class SendBreakoutUsersUpdate(meetingId: String) extends InMessage
|
||||
// Sent by user to request ending all the breakout rooms
|
||||
case class EndAllBreakoutRooms(meetingId: String) extends InMessage
|
||||
// Sent by breakout actor to tell meeting actor that breakout room has been ended
|
||||
case class BreakoutRoomEnded(meetingId: String, breakoutRoomId: String) extends InMessage
|
||||
// Sent by user actor to ask for voice conference transfer
|
||||
case class TransferUserToMeetingRequest(meetingId: String, targetMeetingId: String, userId: String) extends InMessage
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
// Lock
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
case class LockUser(meetingID: String, userId: String, lock: Boolean) extends InMessage
|
||||
case class InitLockSettings(meetingID: String, settings: Permissions) extends InMessage
|
||||
case class SetLockSettings(meetingID: String, setByUser: String, settings: Permissions) extends InMessage
|
||||
case class GetLockSettings(meetingID: String, userId: String) extends InMessage
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
// Users
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
case class ValidateAuthToken(meetingID: String, userId: String, token: String,
|
||||
correlationId: String, sessionId: String) extends InMessage
|
||||
case class RegisterUser(meetingID: String, userID: String, name: String, role: Role,
|
||||
@ -41,7 +91,10 @@ case class AssignPresenter(meetingID: String, newPresenterID: String, newPresent
|
||||
case class SetRecordingStatus(meetingID: String, userId: String, recording: Boolean) extends InMessage
|
||||
case class GetRecordingStatus(meetingID: String, userId: String) extends InMessage
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
// Chat
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
case class GetChatHistoryRequest(meetingID: String, requesterID: String, replyTo: String) extends InMessage
|
||||
case class SendPublicMessageRequest(meetingID: String, requesterID: String, message: Map[String, String]) extends InMessage
|
||||
case class SendPrivateMessageRequest(meetingID: String, requesterID: String, message: Map[String, String]) extends InMessage
|
||||
@ -50,14 +103,20 @@ case class UserConnectedToGlobalAudio(meetingID: String, /** Not used. Just to s
|
||||
case class UserDisconnectedFromGlobalAudio(meetingID: String, /** Not used. Just to satisfy trait **/ voiceConf: String,
|
||||
userid: String, name: String) extends InMessage
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
// Layout
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
case class GetCurrentLayoutRequest(meetingID: String, requesterID: String) extends InMessage
|
||||
case class SetLayoutRequest(meetingID: String, requesterID: String, layoutID: String) extends InMessage
|
||||
case class LockLayoutRequest(meetingID: String, setById: String, lock: Boolean, viewersOnly: Boolean,
|
||||
layout: Option[String]) extends InMessage
|
||||
case class BroadcastLayoutRequest(meetingID: String, requesterID: String, layout: String) extends InMessage
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
// Presentation
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
case class ClearPresentation(meetingID: String) extends InMessage
|
||||
case class RemovePresentation(meetingID: String, presentationID: String) extends InMessage
|
||||
case class GetPresentationInfo(meetingID: String, requesterID: String, replyTo: String) extends InMessage
|
||||
@ -77,7 +136,10 @@ case class PresentationSlideGenerated(meetingID: String, messageKey: String, cod
|
||||
case class PresentationConversionCompleted(meetingID: String, messageKey: String, code: String,
|
||||
presentation: Presentation) extends InMessage
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
// Polling
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//case class CreatePollRequest(meetingID: String, requesterId: String, pollId: String, pollType: String) extends InMessage
|
||||
case class StartCustomPollRequest(meetingID: String, requesterId: String, pollType: String, answers: Seq[String]) extends InMessage
|
||||
case class StartPollRequest(meetingID: String, requesterId: String, pollType: String) extends InMessage
|
||||
@ -88,7 +150,10 @@ case class RespondToPollRequest(meetingID: String, requesterId: String, pollId:
|
||||
case class GetPollRequest(meetingID: String, requesterId: String, pollId: String) extends InMessage
|
||||
case class GetCurrentPollRequest(meetingID: String, requesterId: String) extends InMessage
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// Voice
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
case class InitAudioSettings(meetingID: String, requesterID: String, muted: Boolean) extends InMessage
|
||||
case class SendVoiceUsersRequest(meetingID: String, requesterID: String) extends InMessage
|
||||
case class MuteAllExceptPresenterRequest(meetingID: String, requesterID: String, mute: Boolean) extends InMessage
|
||||
@ -100,14 +165,17 @@ case class EjectUserFromVoiceRequest(meetingID: String, userId: String, ejectedB
|
||||
case class VoiceUserJoinedMessage(meetingID: String, user: String, voiceConfId: String,
|
||||
callerIdNum: String, callerIdName: String, muted: Boolean, talking: Boolean) extends InMessage
|
||||
case class UserJoinedVoiceConfMessage(voiceConfId: String, voiceUserId: String, userId: String, externUserId: String,
|
||||
callerIdName: String, callerIdNum: String, muted: Boolean, talking: Boolean, avatarURL: String, listenOnly: Boolean)
|
||||
case class UserLeftVoiceConfMessage(voiceConfId: String, voiceUserId: String)
|
||||
case class UserLockedInVoiceConfMessage(voiceConfId: String, voiceUserId: String, locked: Boolean)
|
||||
case class UserMutedInVoiceConfMessage(voiceConfId: String, voiceUserId: String, muted: Boolean)
|
||||
case class UserTalkingInVoiceConfMessage(voiceConfId: String, voiceUserId: String, talking: Boolean)
|
||||
case class VoiceConfRecordingStartedMessage(voiceConfId: String, recordStream: String, recording: Boolean, timestamp: String)
|
||||
callerIdName: String, callerIdNum: String, muted: Boolean, talking: Boolean, avatarURL: String, listenOnly: Boolean) extends InMessage
|
||||
case class UserLeftVoiceConfMessage(voiceConfId: String, voiceUserId: String) extends InMessage
|
||||
case class UserLockedInVoiceConfMessage(voiceConfId: String, voiceUserId: String, locked: Boolean) extends InMessage
|
||||
case class UserMutedInVoiceConfMessage(voiceConfId: String, voiceUserId: String, muted: Boolean) extends InMessage
|
||||
case class UserTalkingInVoiceConfMessage(voiceConfId: String, voiceUserId: String, talking: Boolean) extends InMessage
|
||||
case class VoiceConfRecordingStartedMessage(voiceConfId: String, recordStream: String, recording: Boolean, timestamp: String) extends InMessage
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
// Whiteboard
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
case class SendWhiteboardAnnotationRequest(meetingID: String, requesterID: String, annotation: AnnotationVO) extends InMessage
|
||||
case class GetWhiteboardShapesRequest(meetingID: String, requesterID: String, whiteboardId: String, replyTo: String) extends InMessage
|
||||
case class ClearWhiteboardRequest(meetingID: String, requesterID: String, whiteboardId: String) extends InMessage
|
||||
@ -116,10 +184,14 @@ case class EnableWhiteboardRequest(meetingID: String, requesterID: String, enabl
|
||||
case class IsWhiteboardEnabledRequest(meetingID: String, requesterID: String, replyTo: String) extends InMessage
|
||||
case class GetAllMeetingsRequest(meetingID: String /** Not used. Just to satisfy trait **/ ) extends InMessage
|
||||
|
||||
// Caption
|
||||
case class SendCaptionHistoryRequest(meetingID: String, requesterID: String) extends InMessage
|
||||
case class UpdateCaptionOwnerRequest(meetingID: String, locale: String, ownerID: String) extends InMessage
|
||||
case class EditCaptionHistoryRequest(meetingID: String, userID: String, startIndex: Integer, endIndex: Integer, locale: String, text: String) extends InMessage
|
||||
// DeskShare
|
||||
case class DeskShareStartedRequest(conferenceName: String, callerId: String, callerIdName: String)
|
||||
case class DeskShareStoppedRequest(conferenceName: String, callerId: String, callerIdName: String)
|
||||
case class DeskShareRTMPBroadcastStartedRequest(conferenceName: String, streamname: String, videoWidth: Int, videoHeight: Int, timestamp: String)
|
||||
case class DeskShareRTMPBroadcastStoppedRequest(conferenceName: String, streamname: String, videoWidth: Int, videoHeight: Int, timestamp: String)
|
||||
case class DeskShareGetDeskShareInfoRequest(conferenceName: String, requesterID: String, replyTo: String)
|
||||
case class DeskShareStartedRequest(conferenceName: String, callerId: String, callerIdName: String) extends InMessage
|
||||
case class DeskShareStoppedRequest(conferenceName: String, callerId: String, callerIdName: String) extends InMessage
|
||||
case class DeskShareRTMPBroadcastStartedRequest(conferenceName: String, streamname: String, videoWidth: Int, videoHeight: Int, timestamp: String) extends InMessage
|
||||
case class DeskShareRTMPBroadcastStoppedRequest(conferenceName: String, streamname: String, videoWidth: Int, videoHeight: Int, timestamp: String) extends InMessage
|
||||
case class DeskShareGetDeskShareInfoRequest(conferenceName: String, requesterID: String, replyTo: String) extends InMessage
|
||||
|
||||
|
@ -162,5 +162,11 @@ object MessageNames {
|
||||
val MEETING_DESTROYED_EVENT = "meeting_destroyed_event"
|
||||
val KEEP_ALIVE_REPLY = "keep_alive_reply"
|
||||
val USER_LISTEN_ONLY = "user_listening_only"
|
||||
val GET_ALL_MEETINGS_REPLY = "get_all_meetings_reply"
|
||||
}
|
||||
val GET_ALL_MEETINGS_REPLY = "get_all_meetings_reply_message"
|
||||
val EDIT_CAPTION_HISTORY = "edit_caption_history_message"
|
||||
val UPDATE_CAPTION_OWNER = "update_caption_owner_message"
|
||||
val SEND_CAPTION_HISTORY_REPLY = "send_caption_history_reply_message"
|
||||
|
||||
// breakout rooms
|
||||
val BREAKOUT_ROOM_STARTED = "BreakoutRoomStarted"
|
||||
}
|
||||
|
@ -8,15 +8,14 @@ import org.bigbluebutton.core.MeetingProperties
|
||||
import org.bigbluebutton.core.apps.PollVO
|
||||
import org.bigbluebutton.core.apps.SimplePollOutVO
|
||||
import org.bigbluebutton.core.apps.SimplePollResultOutVO
|
||||
|
||||
abstract class OutMessage
|
||||
import org.bigbluebutton.core.apps.BreakoutUser
|
||||
|
||||
case class VoiceRecordingStarted(meetingID: String, recorded: Boolean, recordingFile: String, timestamp: String, confNum: String) extends IOutMessage
|
||||
case class VoiceRecordingStopped(meetingID: String, recorded: Boolean, recordingFile: String, timestamp: String, confNum: String) extends IOutMessage
|
||||
case class RecordingStatusChanged(meetingID: String, recorded: Boolean, userId: String, recording: Boolean) extends IOutMessage
|
||||
case class GetRecordingStatusReply(meetingID: String, recorded: Boolean, userId: String, recording: Boolean) extends IOutMessage
|
||||
case class MeetingCreated(meetingID: String, externalMeetingID: String, recorded: Boolean, name: String,
|
||||
voiceBridge: String, duration: Long, moderatorPass: String, viewerPass: String, createTime: Long, createDate: String) extends IOutMessage
|
||||
voiceBridge: String, duration: Int, moderatorPass: String, viewerPass: String, createTime: Long, createDate: String) extends IOutMessage
|
||||
case class MeetingMuted(meetingID: String, recorded: Boolean, meetingMuted: Boolean) extends IOutMessage
|
||||
case class MeetingEnded(meetingID: String, recorded: Boolean, voiceBridge: String) extends IOutMessage
|
||||
case class MeetingState(meetingID: String, recorded: Boolean, userId: String, permissions: Permissions, meetingMuted: Boolean) extends IOutMessage
|
||||
@ -28,6 +27,21 @@ case class KeepAliveMessageReply(aliveID: String) extends IOutMessage
|
||||
case class PubSubPong(system: String, timestamp: Long) extends IOutMessage
|
||||
case object IsAliveMessage extends IOutMessage
|
||||
|
||||
// Breakout Rooms
|
||||
case class BreakoutRoomsListOutMessage(meetingId: String, rooms: Vector[BreakoutRoomBody]) extends IOutMessage
|
||||
case class CreateBreakoutRoom(meetingId: String, recorded: Boolean, room: BreakoutRoomOutPayload) extends IOutMessage
|
||||
case class EndBreakoutRoom(breakoutId: String) extends IOutMessage
|
||||
case class BreakoutRoomOutPayload(breakoutId: String, name: String, parentId: String,
|
||||
voiceConfId: String, durationInMinutes: Int, moderatorPassword: String, viewerPassword: String,
|
||||
defaultPresentationURL: String)
|
||||
case class BreakoutRoomJoinURLOutMessage(meetingId: String, recorded: Boolean, breakoutId: String, userId: String, joinURL: String) extends IOutMessage
|
||||
case class BreakoutRoomStartedOutMessage(meetingId: String, recorded: Boolean, breakout: BreakoutRoomBody) extends IOutMessage
|
||||
case class BreakoutRoomBody(name: String, breakoutId: String)
|
||||
case class UpdateBreakoutUsersOutMessage(meetingId: String, recorded: Boolean, breakoutId: String, users: Vector[BreakoutUser]) extends IOutMessage
|
||||
case class MeetingTimeRemainingUpdate(meetingId: String, recorded: Boolean, timeRemaining: Int) extends IOutMessage
|
||||
case class BreakoutRoomsTimeRemainingUpdateOutMessage(meetingId: String, recorded: Boolean, timeRemaining: Int) extends IOutMessage
|
||||
case class BreakoutRoomEndedOutMessage(meetingId: String, breakoutId: String) extends IOutMessage
|
||||
|
||||
// Permissions
|
||||
case class PermissionsSettingInitialized(meetingID: String, permissions: Permissions, applyTo: Array[UserVO]) extends IOutMessage
|
||||
case class NewPermissionsSetting(meetingID: String, setByUser: String, permissions: Permissions, applyTo: Array[UserVO]) extends IOutMessage
|
||||
@ -56,6 +70,7 @@ case class MuteVoiceUser(meetingID: String, recorded: Boolean, requesterID: Stri
|
||||
case class UserVoiceMuted(meetingID: String, recorded: Boolean, confNum: String, user: UserVO) extends IOutMessage
|
||||
case class UserVoiceTalking(meetingID: String, recorded: Boolean, confNum: String, user: UserVO) extends IOutMessage
|
||||
case class EjectVoiceUser(meetingID: String, recorded: Boolean, requesterID: String, userId: String, voiceConfId: String, voiceUserId: String) extends IOutMessage
|
||||
case class TransferUserToMeeting(voiceConfId: String, targetVoiceConfId: String, userId: String) extends IOutMessage
|
||||
case class UserJoinedVoice(meetingID: String, recorded: Boolean, confNum: String, user: UserVO) extends IOutMessage
|
||||
case class UserLeftVoice(meetingID: String, recorded: Boolean, confNum: String, user: UserVO) extends IOutMessage
|
||||
|
||||
@ -130,6 +145,10 @@ case class WhiteboardEnabledEvent(meetingID: String, recorded: Boolean, requeste
|
||||
case class IsWhiteboardEnabledReply(meetingID: String, recorded: Boolean, requesterID: String, enabled: Boolean, replyTo: String) extends IOutMessage
|
||||
case class GetAllMeetingsReply(meetings: Array[MeetingInfo]) extends IOutMessage
|
||||
|
||||
// Chat
|
||||
case class SendCaptionHistoryReply(meetingID: String, recorded: Boolean, requesterID: String, history: Map[String, Array[String]]) extends IOutMessage
|
||||
case class UpdateCaptionOwnerReply(meetingID: String, recorded: Boolean, locale: String, ownerID: String) extends IOutMessage
|
||||
case class EditCaptionHistoryReply(meetingID: String, recorded: Boolean, userID: String, startIndex: Integer, endIndex: Integer, locale: String, text: String) extends IOutMessage
|
||||
// DeskShare
|
||||
case class DeskShareStartRTMPBroadcast(conferenceName: String, streamPath: String) extends IOutMessage
|
||||
case class DeskShareStopRTMPBroadcast(conferenceName: String, streamPath: String) extends IOutMessage
|
||||
|
227
akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/BreakoutRoomApp.scala
Executable file
227
akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/BreakoutRoomApp.scala
Executable file
@ -0,0 +1,227 @@
|
||||
package org.bigbluebutton.core.apps
|
||||
|
||||
import org.bigbluebutton.core.api._
|
||||
import scala.collection.mutable.ArrayBuffer
|
||||
import org.bigbluebutton.core.OutMessageGateway
|
||||
import org.bigbluebutton.SystemConfiguration
|
||||
import org.apache.commons.codec.digest.DigestUtils
|
||||
import scala.collection._
|
||||
import scala.collection.SortedSet
|
||||
import org.apache.commons.lang3.StringUtils
|
||||
import java.net.URLEncoder
|
||||
import scala.collection.immutable.StringOps
|
||||
import org.bigbluebutton.core.LiveMeeting
|
||||
import org.bigbluebutton.core.bus.IncomingEventBus
|
||||
import org.bigbluebutton.core.bus.BigBlueButtonEvent
|
||||
|
||||
trait BreakoutRoomApp extends SystemConfiguration {
|
||||
this: LiveMeeting =>
|
||||
|
||||
val outGW: OutMessageGateway
|
||||
val eventBus: IncomingEventBus
|
||||
|
||||
def getDefaultPresentationURL(): String = {
|
||||
var presURL = bbbWebDefaultPresentationURL
|
||||
val page = presModel.getCurrentPage()
|
||||
page foreach { p =>
|
||||
presURL = BreakoutRoomsUtil.fromSWFtoPDF(p.swfUri)
|
||||
}
|
||||
presURL
|
||||
}
|
||||
|
||||
def handleBreakoutRoomsList(msg: BreakoutRoomsListMessage) {
|
||||
val breakoutRooms = breakoutModel.getRooms().toVector map { r => new BreakoutRoomBody(r.name, r.id) }
|
||||
outGW.send(new BreakoutRoomsListOutMessage(mProps.meetingID, breakoutRooms));
|
||||
}
|
||||
|
||||
def handleCreateBreakoutRooms(msg: CreateBreakoutRooms) {
|
||||
var i = 0
|
||||
for (room <- msg.rooms) {
|
||||
i += 1
|
||||
val presURL = bbbWebDefaultPresentationURL
|
||||
val breakoutMeetingId = BreakoutRoomsUtil.createMeetingId(mProps.meetingID, i)
|
||||
val voiceConfId = BreakoutRoomsUtil.createVoiceConfId(mProps.voiceBridge, i)
|
||||
val r = breakoutModel.createBreakoutRoom(breakoutMeetingId, room.name, voiceConfId, room.users, presURL)
|
||||
val p = new BreakoutRoomOutPayload(r.id, r.name, mProps.meetingID,
|
||||
r.voiceConfId, msg.durationInMinutes, bbbWebModeratorPassword, bbbWebViewerPassword,
|
||||
r.defaultPresentationURL)
|
||||
outGW.send(new CreateBreakoutRoom(mProps.meetingID, mProps.recorded, p))
|
||||
}
|
||||
meetingModel.breakoutRoomsdurationInMinutes = msg.durationInMinutes;
|
||||
meetingModel.breakoutRoomsStartedOn = timeNowInSeconds;
|
||||
}
|
||||
|
||||
def sendJoinURL(userId: String, breakoutId: String) {
|
||||
for {
|
||||
user <- usersModel.getUser(userId)
|
||||
apiCall = "join"
|
||||
params = BreakoutRoomsUtil.joinParams(user.name, userId, true, breakoutId, bbbWebModeratorPassword, true)
|
||||
baseString = BreakoutRoomsUtil.createBaseString(params)
|
||||
checksum = BreakoutRoomsUtil.calculateChecksum(apiCall, baseString, bbbWebSharedSecret)
|
||||
joinURL = BreakoutRoomsUtil.createJoinURL(bbbWebAPI, apiCall, baseString, checksum)
|
||||
} yield outGW.send(new BreakoutRoomJoinURLOutMessage(mProps.meetingID, mProps.recorded, breakoutId, userId, joinURL))
|
||||
}
|
||||
|
||||
def handleRequestBreakoutJoinURL(msg: RequestBreakoutJoinURLInMessage) {
|
||||
sendJoinURL(msg.userId, msg.breakoutId)
|
||||
}
|
||||
|
||||
def handleBreakoutRoomCreated(msg: BreakoutRoomCreated) {
|
||||
val room = breakoutModel.getBreakoutRoom(msg.breakoutRoomId)
|
||||
room foreach { room =>
|
||||
sendBreakoutRoomStarted(mProps.meetingID, room.name, room.id, room.voiceConfId)
|
||||
}
|
||||
|
||||
breakoutModel.getAssignedUsers(msg.breakoutRoomId) foreach { users =>
|
||||
users.foreach { u =>
|
||||
log.debug("## Sending Join URL for users: {}", u);
|
||||
sendJoinURL(u, msg.breakoutRoomId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def sendBreakoutRoomStarted(meetingId: String, breakoutName: String, breakoutId: String, voiceConfId: String) {
|
||||
outGW.send(new BreakoutRoomStartedOutMessage(meetingId, mProps.recorded, new BreakoutRoomBody(breakoutName, breakoutId)))
|
||||
}
|
||||
|
||||
def handleBreakoutRoomEnded(msg: BreakoutRoomEnded) {
|
||||
breakoutModel.remove(msg.breakoutRoomId)
|
||||
outGW.send(new BreakoutRoomEndedOutMessage(msg.meetingId, msg.breakoutRoomId))
|
||||
}
|
||||
|
||||
def handleBreakoutRoomUsersUpdate(msg: BreakoutRoomUsersUpdate) {
|
||||
breakoutModel.updateBreakoutUsers(msg.breakoutId, msg.users) foreach { room =>
|
||||
outGW.send(new UpdateBreakoutUsersOutMessage(mProps.meetingID, mProps.recorded, msg.breakoutId, room.users))
|
||||
}
|
||||
}
|
||||
|
||||
def handleSendBreakoutUsersUpdate(msg: SendBreakoutUsersUpdate) {
|
||||
val users = usersModel.getUsers().toVector
|
||||
val breakoutUsers = users map { u => new BreakoutUser(u.externUserID, u.name) }
|
||||
eventBus.publish(BigBlueButtonEvent(mProps.externalMeetingID,
|
||||
new BreakoutRoomUsersUpdate(mProps.externalMeetingID, mProps.meetingID, breakoutUsers)))
|
||||
}
|
||||
|
||||
def handleTransferUserToMeeting(msg: TransferUserToMeetingRequest) {
|
||||
var targetVoiceBridge: String = msg.targetMeetingId
|
||||
// If the current room is a parent room we fetch the voice bridge from the breakout room
|
||||
if (!mProps.isBreakout) {
|
||||
breakoutModel.getBreakoutRoom(msg.targetMeetingId) match {
|
||||
case Some(b) => {
|
||||
targetVoiceBridge = b.voiceConfId;
|
||||
}
|
||||
case None => // do nothing
|
||||
}
|
||||
} // if it is a breakout room, the target voice bridge is the same after removing the last digit
|
||||
else {
|
||||
targetVoiceBridge = mProps.voiceBridge.dropRight(1)
|
||||
}
|
||||
// We check the iser from the mode
|
||||
usersModel.getUser(msg.userId) match {
|
||||
case Some(u) => {
|
||||
if (u.voiceUser.joined) {
|
||||
log.info("Transferring user userId=" + u.userID + " from voiceBridge=" + mProps.voiceBridge + " to targetVoiceConf=" + targetVoiceBridge)
|
||||
outGW.send(new TransferUserToMeeting(mProps.voiceBridge, targetVoiceBridge, u.voiceUser.userId))
|
||||
}
|
||||
}
|
||||
case None => // do nothing
|
||||
}
|
||||
}
|
||||
|
||||
def handleEndAllBreakoutRooms(msg: EndAllBreakoutRooms) {
|
||||
log.info("EndAllBreakoutRooms event received for meetingId={}", mProps.meetingID)
|
||||
breakoutModel.getRooms().foreach { room =>
|
||||
outGW.send(new EndBreakoutRoom(room.id))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
object BreakoutRoomsUtil {
|
||||
def createMeetingId(id: String, index: Int): String = {
|
||||
id.concat("-").concat(index.toString())
|
||||
}
|
||||
|
||||
def createVoiceConfId(id: String, index: Int): String = {
|
||||
id.concat(index.toString())
|
||||
}
|
||||
|
||||
def fromSWFtoPDF(swfURL: String): String = {
|
||||
swfURL.replace("swf", "pdf")
|
||||
}
|
||||
|
||||
def createJoinURL(webAPI: String, apiCall: String, baseString: String, checksum: String): String = {
|
||||
var apiURL = if (webAPI.endsWith("/")) webAPI else webAPI.concat("/")
|
||||
apiURL.concat(apiCall).concat("?").concat(baseString).concat("&checksum=").concat(checksum)
|
||||
}
|
||||
|
||||
//
|
||||
//checksum() -- Return a checksum based on SHA-1 digest
|
||||
//
|
||||
def checksum(s: String): String = {
|
||||
DigestUtils.sha1Hex(s);
|
||||
}
|
||||
|
||||
def calculateChecksum(apiCall: String, baseString: String, sharedSecret: String): String = {
|
||||
checksum(apiCall.concat(baseString).concat(sharedSecret))
|
||||
}
|
||||
|
||||
def joinParams(username: String, userId: String, isBreakout: Boolean, breakoutId: String,
|
||||
password: String, redirect: Boolean): mutable.Map[String, String] = {
|
||||
val params = new collection.mutable.HashMap[String, String]
|
||||
params += "fullName" -> urlEncode(username)
|
||||
params += "userID" -> urlEncode(userId + "-" + breakoutId.substring(breakoutId.lastIndexOf("-") + 1));
|
||||
params += "isBreakout" -> urlEncode(isBreakout.toString())
|
||||
params += "meetingID" -> urlEncode(breakoutId)
|
||||
params += "password" -> urlEncode(password)
|
||||
params += "redirect" -> urlEncode(redirect.toString())
|
||||
|
||||
params
|
||||
}
|
||||
|
||||
def sortParams(params: mutable.Map[String, String]): SortedSet[String] = {
|
||||
collection.immutable.SortedSet[String]() ++ params.keySet
|
||||
}
|
||||
|
||||
//From the list of parameters we want to pass. Creates a base string with parameters
|
||||
//sorted in alphabetical order for us to sign.
|
||||
def createBaseString(params: mutable.Map[String, String]): String = {
|
||||
val csbuf = new StringBuffer()
|
||||
val keys = sortParams(params)
|
||||
|
||||
var first = true;
|
||||
for (key <- keys) {
|
||||
for (value <- params.get(key)) {
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
csbuf.append("&");
|
||||
}
|
||||
|
||||
csbuf.append(key);
|
||||
csbuf.append("=");
|
||||
csbuf.append(value);
|
||||
}
|
||||
}
|
||||
|
||||
return csbuf.toString();
|
||||
}
|
||||
|
||||
def urlEncode(s: String): String = {
|
||||
URLEncoder.encode(s, "UTF-8");
|
||||
}
|
||||
|
||||
//
|
||||
//encodeURIComponent() -- Java encoding similiar to JavaScript encodeURIComponent
|
||||
//
|
||||
def encodeURIComponent(component: String): String = {
|
||||
URLEncoder.encode(component, "UTF-8")
|
||||
.replaceAll("\\%28", "(")
|
||||
.replaceAll("\\%29", ")")
|
||||
.replaceAll("\\+", "%20")
|
||||
.replaceAll("\\%27", "'")
|
||||
.replaceAll("\\%21", "!")
|
||||
.replaceAll("\\%7E", "~")
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
package org.bigbluebutton.core.apps
|
||||
|
||||
import scala.collection.mutable.ArrayBuffer
|
||||
import scala.collection.immutable.HashMap
|
||||
|
||||
case class BreakoutUser(id: String, name: String)
|
||||
case class BreakoutRoom(id: String, name: String, voiceConfId: String,
|
||||
assignedUsers: Vector[String], users: Vector[BreakoutUser], defaultPresentationURL: String)
|
||||
|
||||
class BreakoutRoomModel {
|
||||
private var rooms = new collection.immutable.HashMap[String, BreakoutRoom]
|
||||
|
||||
def add(room: BreakoutRoom): BreakoutRoom = {
|
||||
rooms += room.id -> room
|
||||
room
|
||||
}
|
||||
|
||||
def remove(id: String) {
|
||||
rooms -= id
|
||||
}
|
||||
|
||||
def createBreakoutRoom(id: String, name: String, voiceConfId: String,
|
||||
assignedUsers: Vector[String], defaultPresentationURL: String): BreakoutRoom = {
|
||||
val room = new BreakoutRoom(id, name, voiceConfId, assignedUsers, Vector(), defaultPresentationURL)
|
||||
add(room)
|
||||
}
|
||||
|
||||
def getBreakoutRoom(id: String): Option[BreakoutRoom] = {
|
||||
rooms.get(id)
|
||||
}
|
||||
|
||||
def getRooms(): Array[BreakoutRoom] = {
|
||||
rooms.values.toArray
|
||||
}
|
||||
|
||||
def getAssignedUsers(breakoutId: String): Option[Vector[String]] = {
|
||||
for {
|
||||
room <- rooms.get(breakoutId)
|
||||
} yield room.assignedUsers
|
||||
}
|
||||
|
||||
def updateBreakoutUsers(breakoutId: String, users: Vector[BreakoutUser]): Option[BreakoutRoom] = {
|
||||
for {
|
||||
room <- rooms.get(breakoutId)
|
||||
newroom = room.copy(users = users)
|
||||
room2 = add(newroom)
|
||||
} yield room2
|
||||
}
|
||||
}
|
||||
|
58
akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/CaptionApp.scala
Executable file
58
akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/CaptionApp.scala
Executable file
@ -0,0 +1,58 @@
|
||||
package org.bigbluebutton.core.apps
|
||||
|
||||
import org.bigbluebutton.core.api._
|
||||
import scala.collection.mutable.ArrayBuffer
|
||||
import org.bigbluebutton.core.MeetingActor
|
||||
import org.bigbluebutton.core.OutMessageGateway
|
||||
import org.bigbluebutton.core.LiveMeeting
|
||||
|
||||
trait CaptionApp {
|
||||
this: LiveMeeting =>
|
||||
|
||||
val outGW: OutMessageGateway
|
||||
|
||||
def handleSendCaptionHistoryRequest(msg: SendCaptionHistoryRequest) {
|
||||
var history = captionModel.getHistory()
|
||||
|
||||
outGW.send(new SendCaptionHistoryReply(mProps.meetingID, mProps.recorded, msg.requesterID, history))
|
||||
}
|
||||
|
||||
def handleUpdateCaptionOwnerRequest(msg: UpdateCaptionOwnerRequest) {
|
||||
// clear owner from previous locale
|
||||
if (msg.ownerID.length > 0) {
|
||||
captionModel.findLocaleByOwnerId(msg.ownerID).foreach(t => {
|
||||
captionModel.changeTranscriptOwner(t, "")
|
||||
|
||||
// send notification that owner has changed
|
||||
outGW.send(new UpdateCaptionOwnerReply(mProps.meetingID, mProps.recorded, t, ""))
|
||||
})
|
||||
}
|
||||
// create the locale if it doesn't exist
|
||||
if (captionModel.transcripts contains msg.locale) {
|
||||
captionModel.changeTranscriptOwner(msg.locale, msg.ownerID)
|
||||
} else { // change the owner if it does exist
|
||||
captionModel.newTranscript(msg.locale, msg.ownerID)
|
||||
}
|
||||
|
||||
outGW.send(new UpdateCaptionOwnerReply(mProps.meetingID, mProps.recorded, msg.locale, msg.ownerID))
|
||||
}
|
||||
|
||||
def handleEditCaptionHistoryRequest(msg: EditCaptionHistoryRequest) {
|
||||
captionModel.findLocaleByOwnerId(msg.userID).foreach(t => {
|
||||
if (t == msg.locale) {
|
||||
captionModel.editHistory(msg.startIndex, msg.endIndex, msg.locale, msg.text)
|
||||
|
||||
outGW.send(new EditCaptionHistoryReply(mProps.meetingID, mProps.recorded, msg.userID, msg.startIndex, msg.endIndex, msg.locale, msg.text))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
def checkCaptionOwnerLogOut(userId: String) {
|
||||
captionModel.findLocaleByOwnerId(userId).foreach(t => {
|
||||
captionModel.changeTranscriptOwner(t, "")
|
||||
|
||||
// send notification that owner has changed
|
||||
outGW.send(new UpdateCaptionOwnerReply(mProps.meetingID, mProps.recorded, t, ""))
|
||||
})
|
||||
}
|
||||
}
|
53
akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/CaptionModel.scala
Executable file
53
akka-bbb-apps/src/main/scala/org/bigbluebutton/core/apps/CaptionModel.scala
Executable file
@ -0,0 +1,53 @@
|
||||
package org.bigbluebutton.core.apps
|
||||
|
||||
import scala.collection.mutable.ArrayBuffer
|
||||
import scala.collection.immutable.HashMap
|
||||
|
||||
class CaptionModel {
|
||||
var transcripts = Map[String, Array[String]]()
|
||||
|
||||
def newTranscript(locale: String, ownerId: String) {
|
||||
transcripts += locale -> Array(ownerId, "")
|
||||
}
|
||||
|
||||
def findLocaleByOwnerId(userId: String): Option[String] = {
|
||||
transcripts.find(_._2(0) == userId).foreach(t => {
|
||||
return Some(t._1)
|
||||
})
|
||||
|
||||
return None
|
||||
}
|
||||
|
||||
def changeTranscriptOwner(locale: String, ownerId: String) {
|
||||
if (transcripts contains locale) {
|
||||
transcripts(locale)(0) = ownerId
|
||||
}
|
||||
}
|
||||
|
||||
def getHistory(): Map[String, Array[String]] = {
|
||||
var history = Map[String, Array[String]]()
|
||||
|
||||
transcripts.foreach(t => {
|
||||
history += t._1 -> Array(t._2(0), t._2(1))
|
||||
})
|
||||
|
||||
history
|
||||
}
|
||||
|
||||
def editHistory(startIndex: Integer, endIndex: Integer, locale: String, text: String) {
|
||||
println("editHistory entered")
|
||||
if (transcripts contains locale) {
|
||||
println("editHistory found locale:" + locale)
|
||||
var oText: String = transcripts(locale)(1)
|
||||
|
||||
if (startIndex >= 0 && endIndex <= oText.length && startIndex <= endIndex) {
|
||||
println("editHistory passed index test")
|
||||
var sText: String = oText.substring(0, startIndex)
|
||||
var eText: String = oText.substring(endIndex)
|
||||
|
||||
transcripts(locale)(1) = (sText + text + eText)
|
||||
println("editHistory new history is: " + transcripts(locale)(1))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -2,11 +2,11 @@ package org.bigbluebutton.core.apps
|
||||
|
||||
import org.bigbluebutton.core.api._
|
||||
import scala.collection.mutable.ArrayBuffer
|
||||
import org.bigbluebutton.core.MeetingActor
|
||||
import org.bigbluebutton.core.OutMessageGateway
|
||||
import org.bigbluebutton.core.LiveMeeting
|
||||
|
||||
trait ChatApp {
|
||||
this: MeetingActor =>
|
||||
this: LiveMeeting =>
|
||||
|
||||
val outGW: OutMessageGateway
|
||||
|
||||
|
@ -1,12 +1,12 @@
|
||||
package org.bigbluebutton.core.apps
|
||||
|
||||
import org.bigbluebutton.core.api._
|
||||
import org.bigbluebutton.core.MeetingActor
|
||||
import scala.collection.mutable.ArrayBuffer
|
||||
import org.bigbluebutton.core.OutMessageGateway
|
||||
import org.bigbluebutton.core.LiveMeeting
|
||||
|
||||
trait LayoutApp {
|
||||
this: MeetingActor =>
|
||||
this: LiveMeeting =>
|
||||
|
||||
val outGW: OutMessageGateway
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
package org.bigbluebutton.core.apps
|
||||
|
||||
import org.bigbluebutton.core.api._
|
||||
import org.bigbluebutton.core.MeetingActor
|
||||
import scala.collection.mutable.HashMap
|
||||
import scala.collection.mutable.ArrayBuffer
|
||||
import org.bigbluebutton.common.messages.WhiteboardKeyUtil
|
||||
@ -9,9 +8,10 @@ import org.bigbluebutton.common.messages.WhiteboardKeyUtil
|
||||
import com.google.gson.Gson
|
||||
import java.util.ArrayList
|
||||
import org.bigbluebutton.core.OutMessageGateway
|
||||
import org.bigbluebutton.core.LiveMeeting
|
||||
|
||||
trait PollApp {
|
||||
this: MeetingActor =>
|
||||
this: LiveMeeting =>
|
||||
|
||||
val outGW: OutMessageGateway
|
||||
|
||||
@ -46,8 +46,7 @@ trait PollApp {
|
||||
handleRespondToPoll(poll, msg)
|
||||
}
|
||||
case None => {
|
||||
val result = new RequestResult(StatusCodes.NOT_FOUND, Some(Array(ErrorCodes.RESOURCE_NOT_FOUND)))
|
||||
sender ! new RespondToPollReplyMessage(mProps.meetingID, mProps.recorded, result, msg.requesterId, msg.pollId)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -59,8 +58,7 @@ trait PollApp {
|
||||
outGW.send(new PollHideResultMessage(mProps.meetingID, mProps.recorded, msg.requesterId, msg.pollId))
|
||||
}
|
||||
case None => {
|
||||
val result = new RequestResult(StatusCodes.NOT_FOUND, Some(Array(ErrorCodes.RESOURCE_NOT_FOUND)))
|
||||
sender ! new HidePollResultReplyMessage(mProps.meetingID, mProps.recorded, result, msg.requesterId, msg.pollId)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -107,14 +105,13 @@ trait PollApp {
|
||||
for {
|
||||
page <- presModel.getCurrentPage()
|
||||
annotation = new AnnotationVO(poll.id, WhiteboardKeyUtil.DRAW_END_STATUS, WhiteboardKeyUtil.POLL_RESULT_TYPE, shape, page.id)
|
||||
} this.context.self ! new SendWhiteboardAnnotationRequest(mProps.meetingID, msg.requesterId, annotation)
|
||||
} handleSendWhiteboardAnnotationRequest(new SendWhiteboardAnnotationRequest(mProps.meetingID, msg.requesterId, annotation))
|
||||
|
||||
outGW.send(new PollShowResultMessage(mProps.meetingID, mProps.recorded, msg.requesterId, msg.pollId, poll))
|
||||
|
||||
}
|
||||
case None => {
|
||||
val result = new RequestResult(StatusCodes.NOT_FOUND, Some(Array(ErrorCodes.RESOURCE_NOT_FOUND)))
|
||||
sender ! new ShowPollResultReplyMessage(mProps.meetingID, mProps.recorded, result, msg.requesterId, msg.pollId)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -131,8 +128,7 @@ trait PollApp {
|
||||
outGW.send(new PollStoppedMessage(mProps.meetingID, mProps.recorded, msg.requesterId, poll.id))
|
||||
}
|
||||
case None => {
|
||||
val result = new RequestResult(StatusCodes.NOT_FOUND, Some(Array(ErrorCodes.RESOURCE_NOT_FOUND)))
|
||||
sender ! new StopPollReplyMessage(mProps.meetingID, mProps.recorded, result, msg.requesterId)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -152,8 +148,7 @@ trait PollApp {
|
||||
outGW.send(new PollStartedMessage(mProps.meetingID, mProps.recorded, msg.requesterId, pollId, poll))
|
||||
}
|
||||
case None => {
|
||||
val result = new RequestResult(StatusCodes.NOT_FOUND, Some(Array(ErrorCodes.RESOURCE_NOT_FOUND)))
|
||||
sender ! new StartPollReplyMessage(mProps.meetingID, mProps.recorded, result, msg.requesterId, pollId)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -174,8 +169,7 @@ trait PollApp {
|
||||
outGW.send(new PollStartedMessage(mProps.meetingID, mProps.recorded, msg.requesterId, pollId, poll))
|
||||
}
|
||||
case None => {
|
||||
val result = new RequestResult(StatusCodes.NOT_FOUND, Some(Array(ErrorCodes.RESOURCE_NOT_FOUND)))
|
||||
sender ! new StartPollReplyMessage(mProps.meetingID, mProps.recorded, result, msg.requesterId, pollId)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -204,8 +198,7 @@ trait PollApp {
|
||||
|
||||
}
|
||||
case None => {
|
||||
val result = new RequestResult(StatusCodes.FORBIDDEN, Some(Array(ErrorCodes.RESOURCE_NOT_FOUND)))
|
||||
sender ! new RespondToPollReplyMessage(mProps.meetingID, mProps.recorded, result, msg.requesterId, msg.pollId)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,12 @@
|
||||
package org.bigbluebutton.core.apps
|
||||
|
||||
import org.bigbluebutton.core.api._
|
||||
import org.bigbluebutton.core.MeetingActor
|
||||
import com.google.gson.Gson
|
||||
import org.bigbluebutton.core.OutMessageGateway
|
||||
import org.bigbluebutton.core.LiveMeeting
|
||||
|
||||
trait PresentationApp {
|
||||
this: MeetingActor =>
|
||||
this: LiveMeeting =>
|
||||
|
||||
val outGW: OutMessageGateway
|
||||
|
||||
@ -75,8 +75,6 @@ trait PresentationApp {
|
||||
}
|
||||
|
||||
def handleGetPresentationInfo(msg: GetPresentationInfo) {
|
||||
// println("PresentationApp : handleGetPresentationInfo GetPresentationInfo for meeting [" + msg.meetingID + "] [" + msg.requesterID + "]" )
|
||||
|
||||
val curPresenter = usersModel.getCurrentPresenterInfo();
|
||||
val presenter = new CurrentPresenter(curPresenter.presenterID, curPresenter.presenterName, curPresenter.assignedBy)
|
||||
val presentations = presModel.getPresentations
|
||||
@ -108,7 +106,7 @@ trait PresentationApp {
|
||||
// printPresentations
|
||||
|
||||
usersModel.getCurrentPresenter() foreach { pres =>
|
||||
this.context.self ! StopPollRequest(mProps.meetingID, pres.userID)
|
||||
handleStopPollRequest(StopPollRequest(mProps.meetingID, pres.userID))
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -3,13 +3,13 @@ package org.bigbluebutton.core.apps
|
||||
import org.bigbluebutton.core.api._
|
||||
import scala.collection.mutable.HashMap
|
||||
import java.util.ArrayList
|
||||
import org.bigbluebutton.core.MeetingActor
|
||||
import scala.collection.mutable.ArrayBuffer
|
||||
import scala.collection.immutable.ListSet
|
||||
import org.bigbluebutton.core.OutMessageGateway
|
||||
import org.bigbluebutton.core.LiveMeeting
|
||||
|
||||
trait UsersApp {
|
||||
this: MeetingActor =>
|
||||
this: LiveMeeting =>
|
||||
|
||||
val outGW: OutMessageGateway
|
||||
|
||||
@ -111,7 +111,7 @@ trait UsersApp {
|
||||
* Sometimes, the actor seems to hang and doesn't anymore accept messages. This is a simple
|
||||
* audit to check whether the actor is still alive. (ralam feb 25, 2015)
|
||||
*/
|
||||
sender ! new ValidateAuthTokenReply(mProps.meetingID, msg.userId, msg.token, false, msg.correlationId)
|
||||
//sender ! new ValidateAuthTokenReply(mProps.meetingID, msg.userId, msg.token, false, msg.correlationId)
|
||||
}
|
||||
|
||||
def handleRegisterUser(msg: RegisterUser) {
|
||||
@ -415,6 +415,8 @@ trait UsersApp {
|
||||
vu.userId, u.userID, u.externUserID, vu.callerName,
|
||||
vu.callerNum, vu.muted, vu.talking, vu.avatarURL, u.listenOnly));
|
||||
}
|
||||
|
||||
checkCaptionOwnerLogOut(u.userID)
|
||||
}
|
||||
|
||||
startCheckingIfWeNeedToEndVoiceConf()
|
||||
@ -609,7 +611,7 @@ trait UsersApp {
|
||||
|
||||
def assignNewPresenter(newPresenterID: String, newPresenterName: String, assignedBy: String) {
|
||||
// Stop poll if one is running as presenter left.
|
||||
this.context.self ! StopPollRequest(mProps.meetingID, assignedBy)
|
||||
handleStopPollRequest(StopPollRequest(mProps.meetingID, assignedBy))
|
||||
|
||||
if (usersModel.hasUser(newPresenterID)) {
|
||||
|
||||
|
@ -1,14 +1,14 @@
|
||||
package org.bigbluebutton.core.apps
|
||||
|
||||
import org.bigbluebutton.core.api._
|
||||
import org.bigbluebutton.core.MeetingActor
|
||||
import org.bigbluebutton.common.messages.WhiteboardKeyUtil
|
||||
import org.bigbluebutton.core.OutMessageGateway
|
||||
import org.bigbluebutton.core.LiveMeeting
|
||||
|
||||
case class Whiteboard(id: String, shapes: Seq[AnnotationVO])
|
||||
|
||||
trait WhiteboardApp {
|
||||
this: MeetingActor =>
|
||||
this: LiveMeeting =>
|
||||
|
||||
val outGW: OutMessageGateway
|
||||
|
||||
|
@ -0,0 +1,34 @@
|
||||
package org.bigbluebutton.core.bus
|
||||
|
||||
import akka.actor.ActorRef
|
||||
import akka.event.EventBus
|
||||
import akka.event.ActorEventBus
|
||||
import akka.event.LookupClassification
|
||||
import org.bigbluebutton.core.api.InMessage
|
||||
|
||||
case class BigBlueButtonEvent(val topic: String, val payload: InMessage)
|
||||
|
||||
class IncomingEventBus extends EventBus with LookupClassification {
|
||||
type Event = BigBlueButtonEvent
|
||||
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
|
||||
}
|
||||
|
@ -0,0 +1,34 @@
|
||||
package org.bigbluebutton.core.bus
|
||||
|
||||
import akka.actor.ActorRef
|
||||
import akka.event.EventBus
|
||||
import akka.event.ActorEventBus
|
||||
import akka.event.LookupClassification
|
||||
import org.bigbluebutton.core.api._
|
||||
|
||||
case class BigBlueButtonOutMessage(val topic: String, val payload: IOutMessage)
|
||||
|
||||
class OutgoingEventBus extends EventBus with LookupClassification {
|
||||
type Event = BigBlueButtonOutMessage
|
||||
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
|
||||
}
|
||||
|
@ -0,0 +1,44 @@
|
||||
package org.bigbluebutton.core.pubsub.senders
|
||||
|
||||
import org.bigbluebutton.common.messages.Constants
|
||||
import scala.collection.mutable.HashMap
|
||||
import org.bigbluebutton.core.api._
|
||||
import collection.JavaConverters._
|
||||
import scala.collection.JavaConversions._
|
||||
import org.bigbluebutton.core.messaging.Util
|
||||
|
||||
object CaptionMessageToJsonConverter {
|
||||
def sendCaptionHistoryReplyToJson(msg: SendCaptionHistoryReply): String = {
|
||||
val payload = new java.util.HashMap[String, Any]()
|
||||
payload.put(Constants.MEETING_ID, msg.meetingID)
|
||||
payload.put(Constants.REQUESTER_ID, msg.requesterID)
|
||||
|
||||
payload.put(Constants.CAPTION_HISTORY, mapAsJavaMap(msg.history))
|
||||
|
||||
val header = Util.buildHeader(MessageNames.SEND_CAPTION_HISTORY_REPLY, Some(msg.requesterID))
|
||||
Util.buildJson(header, payload)
|
||||
}
|
||||
|
||||
def updateCaptionOwnerReplyToJson(msg: UpdateCaptionOwnerReply): String = {
|
||||
val payload = new java.util.HashMap[String, Any]()
|
||||
payload.put(Constants.MEETING_ID, msg.meetingID)
|
||||
payload.put(Constants.LOCALE, msg.locale)
|
||||
payload.put(Constants.OWNER_ID, msg.ownerID)
|
||||
|
||||
val header = Util.buildHeader(MessageNames.UPDATE_CAPTION_OWNER, None)
|
||||
Util.buildJson(header, payload)
|
||||
}
|
||||
|
||||
def editCaptionHistoryReplyToJson(msg: EditCaptionHistoryReply): String = {
|
||||
val payload = new java.util.HashMap[String, Any]()
|
||||
payload.put(Constants.MEETING_ID, msg.meetingID)
|
||||
payload.put(Constants.USER_ID, msg.userID)
|
||||
payload.put(Constants.START_INDEX, msg.startIndex)
|
||||
payload.put(Constants.END_INDEX, msg.endIndex)
|
||||
payload.put(Constants.LOCALE, msg.locale)
|
||||
payload.put(Constants.TEXT, msg.text)
|
||||
|
||||
val header = Util.buildHeader(MessageNames.EDIT_CAPTION_HISTORY, None)
|
||||
Util.buildJson(header, payload)
|
||||
}
|
||||
}
|
@ -1,9 +1,12 @@
|
||||
package org.bigbluebutton.core.pubsub.senders
|
||||
|
||||
import org.bigbluebutton.core.messaging.Util
|
||||
import org.bigbluebutton.core.api._
|
||||
import com.google.gson.Gson
|
||||
import scala.collection.JavaConverters._
|
||||
import org.bigbluebutton.core.messaging.Util
|
||||
import org.bigbluebutton.messages._
|
||||
|
||||
import spray.json.JsArray
|
||||
import spray.json.JsObject
|
||||
import spray.json.JsString
|
||||
|
||||
object MeetingMessageToJsonConverter {
|
||||
def meetingDestroyedToJson(msg: MeetingDestroyed): String = {
|
||||
@ -129,4 +132,74 @@ object MeetingMessageToJsonConverter {
|
||||
val header = Util.buildHeader(MessageNames.GET_ALL_MEETINGS_REPLY, None)
|
||||
Util.buildJson(header, payload)
|
||||
}
|
||||
}
|
||||
|
||||
def breakoutRoomsListOutMessageToJson(msg: BreakoutRoomsListOutMessage): String = {
|
||||
val payload = new java.util.HashMap[String, Any]()
|
||||
payload.put("meetingId", msg.meetingId)
|
||||
payload.put("rooms", msg.rooms.toArray)
|
||||
|
||||
val header = Util.buildHeader(BreakoutRoomsList.NAME, None)
|
||||
Util.buildJson(header, payload)
|
||||
}
|
||||
|
||||
def breakoutRoomStartedOutMessageToJson(msg: BreakoutRoomStartedOutMessage): String = {
|
||||
val payload = new java.util.HashMap[String, Any]()
|
||||
payload.put("meetingId", msg.meetingId)
|
||||
payload.put("breakoutId", msg.breakout.breakoutId)
|
||||
payload.put("name", msg.breakout.name)
|
||||
|
||||
val header = Util.buildHeader(BreakoutRoomStarted.NAME, None)
|
||||
Util.buildJson(header, payload)
|
||||
}
|
||||
|
||||
def breakoutRoomEndedOutMessageToJson(msg: BreakoutRoomEndedOutMessage): String = {
|
||||
val payload = new java.util.HashMap[String, Any]()
|
||||
payload.put("meetingId", msg.meetingId)
|
||||
payload.put("breakoutId", msg.breakoutId)
|
||||
|
||||
val header = Util.buildHeader(BreakoutRoomClosed.NAME, None)
|
||||
Util.buildJson(header, payload)
|
||||
}
|
||||
|
||||
def breakoutRoomJoinURLOutMessageToJson(msg: BreakoutRoomJoinURLOutMessage): String = {
|
||||
val payload = new java.util.HashMap[String, Any]()
|
||||
payload.put("meetingId", msg.meetingId)
|
||||
payload.put("breakoutId", msg.breakoutId)
|
||||
payload.put("userId", msg.userId)
|
||||
payload.put("joinURL", msg.joinURL)
|
||||
|
||||
val header = Util.buildHeader(BreakoutRoomJoinURL.NAME, None)
|
||||
Util.buildJson(header, payload)
|
||||
}
|
||||
|
||||
def updateBreakoutUsersOutMessageToJson(msg: UpdateBreakoutUsersOutMessage): String = {
|
||||
val payload = new java.util.HashMap[String, Any]()
|
||||
payload.put("meetingId", msg.meetingId)
|
||||
payload.put("breakoutId", msg.breakoutId)
|
||||
payload.put("recorded", msg.recorded)
|
||||
payload.put("users", msg.users.toArray)
|
||||
|
||||
val header = Util.buildHeader(UpdateBreakoutUsers.NAME, None)
|
||||
Util.buildJson(header, payload)
|
||||
}
|
||||
|
||||
def meetingTimeRemainingUpdateToJson(msg: MeetingTimeRemainingUpdate): String = {
|
||||
val payload = new java.util.HashMap[String, Any]()
|
||||
payload.put("meetingId", msg.meetingId)
|
||||
payload.put("recorded", msg.recorded)
|
||||
payload.put("timeRemaining", msg.timeRemaining)
|
||||
|
||||
val header = Util.buildHeader(TimeRemainingUpdate.NAME, None)
|
||||
Util.buildJson(header, payload)
|
||||
}
|
||||
|
||||
def breakoutRoomsTimeRemainingUpdateToJson(msg: BreakoutRoomsTimeRemainingUpdateOutMessage): String = {
|
||||
val payload = new java.util.HashMap[String, Any]()
|
||||
payload.put("meetingId", msg.meetingId)
|
||||
payload.put("recorded", msg.recorded)
|
||||
payload.put("timeRemaining", msg.timeRemaining)
|
||||
|
||||
val header = Util.buildHeader(BreakoutRoomsTimeRemainingUpdate.NAME, None)
|
||||
Util.buildJson(header, payload)
|
||||
}
|
||||
}
|
||||
|
6
akka-bbb-apps/src/test/java/org/bigbluebutton/xml/Simple.java
Executable file
6
akka-bbb-apps/src/test/java/org/bigbluebutton/xml/Simple.java
Executable file
@ -0,0 +1,6 @@
|
||||
package org.bigbluebutton.xml;
|
||||
|
||||
public class Simple {
|
||||
public int x = 1;
|
||||
public int y = 2;
|
||||
}
|
27
akka-bbb-apps/src/test/scala/org/bigbluebutton/core/AppsTestFixtures.scala
Executable file
27
akka-bbb-apps/src/test/scala/org/bigbluebutton/core/AppsTestFixtures.scala
Executable file
@ -0,0 +1,27 @@
|
||||
package org.bigbluebutton.core
|
||||
|
||||
trait AppsTestFixtures {
|
||||
|
||||
val meetingId = "testMeetingId"
|
||||
val externalMeetingId = "testExternalMeetingId"
|
||||
val meetingName = "test meeting"
|
||||
val record = false
|
||||
val voiceConfId = "85115"
|
||||
val durationInMinutes = 10
|
||||
val autoStartRecording = false
|
||||
val allowStartStopRecording = false
|
||||
val moderatorPassword = "modpass"
|
||||
val viewerPassword = "viewpass"
|
||||
val createTime = System.currentTimeMillis
|
||||
val createDate = "Oct 26, 2015"
|
||||
val isBreakout = false
|
||||
|
||||
val mProps = new MeetingProperties(meetingId, externalMeetingId,
|
||||
meetingName, record,
|
||||
voiceConfId,
|
||||
durationInMinutes,
|
||||
autoStartRecording, allowStartStopRecording,
|
||||
moderatorPassword, viewerPassword,
|
||||
createTime, createDate, isBreakout)
|
||||
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
package org.bigbluebutton.core
|
||||
|
||||
trait BreakoutRoomsTestFixtures extends AppsTestFixtures {
|
||||
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
package org.bigbluebutton.core
|
||||
|
||||
import scala.util.Success
|
||||
import scala.util.Failure
|
||||
import org.scalatest._
|
||||
import org.bigbluebutton.core.api.CreateBreakoutRooms
|
||||
|
||||
class JsonMessageDecoderTests extends UnitSpec with JsonMessageFixtures {
|
||||
|
||||
it should "return a CreateBreakoutRoomsRequestMessage" in {
|
||||
JsonMessageDecoder.decode(createBreakoutRoomsRequestMessage) match {
|
||||
case Some(validMsg) => {
|
||||
val vm = validMsg.asInstanceOf[CreateBreakoutRooms]
|
||||
assert(vm.durationInMinutes == 20)
|
||||
}
|
||||
case None => fail("Failed to decode message")
|
||||
}
|
||||
}
|
||||
|
||||
it should "fail to decode CreateBreakoutRoomsRequestMessage" in {
|
||||
JsonMessageDecoder.decode(invalidCreateBreakoutRoomsRequestMessage) match {
|
||||
case Some(validMsg) => fail("Should have failed to decode message")
|
||||
case None => assert(true)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package org.bigbluebutton.core
|
||||
|
||||
trait JsonMessageFixtures {
|
||||
val createBreakoutRoomsRequestMessage = """
|
||||
{"header":{"name":"CreateBreakoutRoomsRequest"},"payload":{"meetingId":"abc123","rooms":[{"name":"room1","users":["Tidora","Nidora","Tinidora"]},{"name":"room2","users":["Jose","Wally","Paolo"]},{"name":"room3","users":["Alden","Yaya Dub"]}],"durationInMinutes":20}}
|
||||
"""
|
||||
|
||||
val invalidCreateBreakoutRoomsRequestMessage = """
|
||||
{"header":{"name":"CreateBreakoutRoomsRequest"},"payload":{"meetingId2":"abc123","rooms":[{"name":"room1","users":["Tidora","Nidora","Tinidora"]},{"name":"room2","users":["Jose","Wally","Paolo"]},{"name":"room3","users":["Alden","Yaya Dub"]}],"durationInMinutes":20}}
|
||||
"""
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
package org.bigbluebutton.core
|
||||
|
||||
trait MeetingManagerTestFixtures extends AppsTestFixtures {
|
||||
|
||||
}
|
153
akka-bbb-apps/src/test/scala/org/bigbluebutton/core/TestKitUsageSpec.scala
Executable file
153
akka-bbb-apps/src/test/scala/org/bigbluebutton/core/TestKitUsageSpec.scala
Executable file
@ -0,0 +1,153 @@
|
||||
package org.bigbluebutton.core
|
||||
|
||||
import scala.util.Random
|
||||
import com.typesafe.config.ConfigFactory
|
||||
import akka.actor.Actor
|
||||
import akka.actor.ActorRef
|
||||
import akka.actor.ActorSystem
|
||||
import akka.actor.Props
|
||||
import akka.testkit.DefaultTimeout
|
||||
import akka.testkit.ImplicitSender
|
||||
import akka.testkit.TestKit
|
||||
import scala.concurrent.duration._
|
||||
import scala.collection.immutable
|
||||
import org.scalatest.matchers.ShouldMatchers
|
||||
import org.scalatest.WordSpecLike
|
||||
import org.scalatest.BeforeAndAfterAll
|
||||
import org.scalatest.Matchers
|
||||
|
||||
/**
|
||||
* a Test to show some TestKit examples
|
||||
*/
|
||||
class TestKitUsageSpec extends TestKit(ActorSystem("TestKitUsageSpec",
|
||||
ConfigFactory.parseString(TestKitUsageSpec.config)))
|
||||
with DefaultTimeout with ImplicitSender with WordSpecLike
|
||||
with Matchers with BeforeAndAfterAll {
|
||||
|
||||
import TestKitUsageSpec._
|
||||
|
||||
val echoRef = system.actorOf(Props[EchoActor])
|
||||
val forwardRef = system.actorOf(Props(classOf[ForwardingActor], testActor))
|
||||
val filterRef = system.actorOf(Props(classOf[FilteringActor], testActor))
|
||||
val randomHead = Random.nextInt(6)
|
||||
val randomTail = Random.nextInt(10)
|
||||
val headList = immutable.Seq().padTo(randomHead, "0")
|
||||
val tailList = immutable.Seq().padTo(randomTail, "1")
|
||||
val seqRef =
|
||||
system.actorOf(Props(classOf[SequencingActor], testActor, headList, tailList))
|
||||
|
||||
override def afterAll {
|
||||
shutdown(system)
|
||||
}
|
||||
|
||||
"An EchoActor" should {
|
||||
"Respond with the same message it receives" in {
|
||||
within(500 millis) {
|
||||
echoRef ! "test"
|
||||
expectMsg("test")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
"A ForwardingActor" should {
|
||||
"Forward a message it receives" in {
|
||||
within(500 millis) {
|
||||
forwardRef ! "test"
|
||||
expectMsg("test")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
"A FilteringActor" should {
|
||||
"Filter all messages, except expected messagetypes it receives" in {
|
||||
var messages = Seq[String]()
|
||||
within(500 millis) {
|
||||
filterRef ! "test"
|
||||
expectMsg("test")
|
||||
filterRef ! 1
|
||||
expectNoMsg
|
||||
filterRef ! "some"
|
||||
filterRef ! "more"
|
||||
filterRef ! 1
|
||||
filterRef ! "text"
|
||||
filterRef ! 1
|
||||
|
||||
receiveWhile(500 millis) {
|
||||
case msg: String => messages = msg +: messages
|
||||
}
|
||||
}
|
||||
messages.length should be(3)
|
||||
messages.reverse should be(Seq("some", "more", "text"))
|
||||
}
|
||||
}
|
||||
"A SequencingActor" should {
|
||||
"receive an interesting message at some point " in {
|
||||
within(500 millis) {
|
||||
ignoreMsg {
|
||||
case msg: String => msg != "something"
|
||||
}
|
||||
seqRef ! "something"
|
||||
expectMsg("something")
|
||||
ignoreMsg {
|
||||
case msg: String => msg == "1"
|
||||
}
|
||||
expectNoMsg
|
||||
ignoreNoMsg
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object TestKitUsageSpec {
|
||||
// Define your test specific configuration here
|
||||
val config = """
|
||||
akka {
|
||||
loglevel = "WARNING"
|
||||
}
|
||||
"""
|
||||
|
||||
/**
|
||||
* An Actor that echoes everything you send to it
|
||||
*/
|
||||
class EchoActor extends Actor {
|
||||
def receive = {
|
||||
case msg => sender ! msg
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An Actor that forwards every message to a next Actor
|
||||
*/
|
||||
class ForwardingActor(next: ActorRef) extends Actor {
|
||||
def receive = {
|
||||
case msg => next ! msg
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An Actor that only forwards certain messages to a next Actor
|
||||
*/
|
||||
class FilteringActor(next: ActorRef) extends Actor {
|
||||
def receive = {
|
||||
case msg: String => next ! msg
|
||||
case _ => None
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An actor that sends a sequence of messages with a random head list, an
|
||||
* interesting value and a random tail list. The idea is that you would
|
||||
* like to test that the interesting value is received and that you cant
|
||||
* be bothered with the rest
|
||||
*/
|
||||
class SequencingActor(next: ActorRef, head: immutable.Seq[String],
|
||||
tail: immutable.Seq[String]) extends Actor {
|
||||
def receive = {
|
||||
case msg => {
|
||||
head foreach { next ! _ }
|
||||
next ! msg
|
||||
tail foreach { next ! _ }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
8
akka-bbb-apps/src/test/scala/org/bigbluebutton/core/UnitSpec.scala
Executable file
8
akka-bbb-apps/src/test/scala/org/bigbluebutton/core/UnitSpec.scala
Executable file
@ -0,0 +1,8 @@
|
||||
package org.bigbluebutton.core
|
||||
|
||||
import org.scalatest.FlatSpec
|
||||
import org.scalatest.BeforeAndAfterAll
|
||||
import org.scalatest.WordSpec
|
||||
import org.scalatest.Matchers
|
||||
|
||||
class UnitSpec extends FlatSpec with Matchers with BeforeAndAfterAll
|
@ -0,0 +1,23 @@
|
||||
package org.bigbluebutton.core.apps
|
||||
|
||||
import collection.mutable.Stack
|
||||
import org.scalatest._
|
||||
import org.bigbluebutton.core.UnitSpec
|
||||
|
||||
class BreakoutRoomModelSpec extends UnitSpec {
|
||||
|
||||
"A Stack" should "pop values in last-in-first-out order" in {
|
||||
val stack = new Stack[Int]
|
||||
stack.push(1)
|
||||
stack.push(2)
|
||||
assert(stack.pop() === 2)
|
||||
assert(stack.pop() === 1)
|
||||
}
|
||||
|
||||
it should "throw NoSuchElementException if an empty stack is popped" in {
|
||||
val emptyStack = new Stack[String]
|
||||
intercept[NoSuchElementException] {
|
||||
emptyStack.pop()
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,74 @@
|
||||
package org.bigbluebutton.core.apps
|
||||
|
||||
import collection.mutable.Stack
|
||||
import org.scalatest._
|
||||
import org.bigbluebutton.core.UnitSpec
|
||||
|
||||
class BreakoutRoomsUtilSpec extends UnitSpec {
|
||||
|
||||
it should "return a pdfURL" in {
|
||||
val baseURL = "http://localhost/pre1/page1."
|
||||
val swfURL = baseURL + "swf"
|
||||
val pdfURL = BreakoutRoomsUtil.fromSWFtoPDF(swfURL)
|
||||
assert(pdfURL == baseURL + "pdf")
|
||||
}
|
||||
|
||||
it should "return a meetingId" in {
|
||||
val mainMeetingId = "abc-123"
|
||||
val index = 1
|
||||
val result = mainMeetingId.concat("-").concat(index.toString())
|
||||
val breakoutMeetingId = BreakoutRoomsUtil.createMeetingId(mainMeetingId, index)
|
||||
assert(breakoutMeetingId == result)
|
||||
}
|
||||
|
||||
it should "return a voiceConfId" in {
|
||||
val voiceConfId = "85115"
|
||||
val index = 1
|
||||
val result = voiceConfId.concat(index.toString())
|
||||
val breakoutMeetingId = BreakoutRoomsUtil.createVoiceConfId(voiceConfId, index)
|
||||
assert(breakoutMeetingId == result)
|
||||
}
|
||||
|
||||
it should "encode the strings properly" in {
|
||||
val username = "User 4621018"
|
||||
val encodedUsername = "User+4621018"
|
||||
val user = BreakoutRoomsUtil.urlEncode(username)
|
||||
assert(user == encodedUsername)
|
||||
}
|
||||
|
||||
it should "create a base string" in {
|
||||
val baseString = "fullName=User+4621018&isBreakout=true&meetingID=random-1853792&password=mp&redirect=true"
|
||||
|
||||
val params = new collection.mutable.HashMap[String, String]
|
||||
params += "fullName" -> BreakoutRoomsUtil.urlEncode("User 4621018")
|
||||
params += "isBreakout" -> BreakoutRoomsUtil.urlEncode("true")
|
||||
params += "meetingID" -> BreakoutRoomsUtil.urlEncode("random-1853792")
|
||||
params += "password" -> BreakoutRoomsUtil.urlEncode("mp")
|
||||
params += "redirect" -> BreakoutRoomsUtil.urlEncode("true")
|
||||
|
||||
val result = BreakoutRoomsUtil.createBaseString(params)
|
||||
assert(result == baseString)
|
||||
}
|
||||
|
||||
it should "calculate the checksum of join url" in {
|
||||
val sharedSecret = "a820d30da2db356124fce5bd5d8054b4"
|
||||
val checksum = "6baef866df491ae82df992eb14f7f8511d5b77f3"
|
||||
val baseString = "fullName=User+4621018&isBreakout=true&meetingID=random-1853792&password=mp&redirect=true"
|
||||
|
||||
val joinChecksum = BreakoutRoomsUtil.calculateChecksum("join", baseString, sharedSecret)
|
||||
assert(joinChecksum == checksum)
|
||||
}
|
||||
|
||||
it should "create a join api url" in {
|
||||
val baseString = "fullName=User+4621018&isBreakout=true&meetingID=random-1853792&password=mp&redirect=true"
|
||||
val webAPI = "http://www.example.com/bigbluebutton/api/"
|
||||
val joinAPI = "join"
|
||||
val checksum = "6baef866df491ae82df992eb14f7f8511d5b77f3"
|
||||
|
||||
val joinURL = webAPI.concat(joinAPI).concat("?").concat(baseString).concat("&checksum=").concat(checksum)
|
||||
val result = BreakoutRoomsUtil.createJoinURL(webAPI, joinAPI, baseString, checksum);
|
||||
assert(joinURL == result)
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -39,4 +39,4 @@
|
||||
# - setup with a configuration tool after unzip
|
||||
# - use the path to the application.ini file
|
||||
# -Dconfig.file=${{path_to}}/conf/application.conf
|
||||
|
||||
-Dconfig.file=/usr/share/bbb-apps-akka/conf/application.conf
|
||||
|
@ -52,7 +52,7 @@ libraryDependencies ++= {
|
||||
"com.google.code.gson" % "gson" % "1.7.1",
|
||||
"redis.clients" % "jedis" % "2.1.0",
|
||||
"org.apache.commons" % "commons-lang3" % "3.2",
|
||||
"org.bigbluebutton" % "bbb-common-message" % "0.0.17",
|
||||
"org.bigbluebutton" % "bbb-common-message" % "0.0.18-SNAPSHOT",
|
||||
"org.bigbluebutton" % "bbb-fsesl-client" % "0.0.4"
|
||||
)}
|
||||
|
||||
|
@ -1,12 +1,12 @@
|
||||
package org.bigbluebutton.freeswitch.pubsub.receivers;
|
||||
|
||||
|
||||
import org.bigbluebutton.common.messages.EjectAllUsersFromVoiceConfRequestMessage;
|
||||
import org.bigbluebutton.common.messages.EjectUserFromVoiceConfRequestMessage;
|
||||
import org.bigbluebutton.common.messages.GetUsersFromVoiceConfRequestMessage;
|
||||
import org.bigbluebutton.common.messages.MuteUserInVoiceConfRequestMessage;
|
||||
import org.bigbluebutton.common.messages.StartRecordingVoiceConfRequestMessage;
|
||||
import org.bigbluebutton.common.messages.StopRecordingVoiceConfRequestMessage;
|
||||
import org.bigbluebutton.common.messages.TransferUserToVoiceConfRequestMessage;
|
||||
import org.bigbluebutton.common.messages.DeskShareStartRTMPBroadcastEventMessage;
|
||||
import org.bigbluebutton.common.messages.DeskShareStopRTMPBroadcastEventMessage;
|
||||
import org.bigbluebutton.common.messages.DeskShareHangUpEventMessage;
|
||||
@ -17,16 +17,18 @@ import com.google.gson.JsonParser;
|
||||
|
||||
public class RedisMessageReceiver {
|
||||
|
||||
public static final String TO_VOICE_CONF_CHANNEL = "bigbluebutton:to-voice-conf";
|
||||
public static final String TO_VOICE_CONF_PATTERN = TO_VOICE_CONF_CHANNEL + ":*";
|
||||
public static final String TO_VOICE_CONF_SYSTEM_CHAN = TO_VOICE_CONF_CHANNEL + ":system";
|
||||
|
||||
public static final String TO_VOICE_CONF_CHANNEL = "bigbluebutton:to-voice-conf";
|
||||
public static final String TO_VOICE_CONF_PATTERN = TO_VOICE_CONF_CHANNEL
|
||||
+ ":*";
|
||||
public static final String TO_VOICE_CONF_SYSTEM_CHAN = TO_VOICE_CONF_CHANNEL
|
||||
+ ":system";
|
||||
|
||||
private final FreeswitchApplication fsApp;
|
||||
|
||||
|
||||
public RedisMessageReceiver(FreeswitchApplication fsApp) {
|
||||
this.fsApp = fsApp;
|
||||
}
|
||||
|
||||
|
||||
public void handleMessage(String pattern, String channel, String message) {
|
||||
if (channel.equalsIgnoreCase(TO_VOICE_CONF_SYSTEM_CHAN)) {
|
||||
JsonParser parser = new JsonParser();
|
||||
@ -38,24 +40,27 @@ public class RedisMessageReceiver {
|
||||
if (header.has("name")) {
|
||||
String messageName = header.get("name").getAsString();
|
||||
switch (messageName) {
|
||||
case EjectAllUsersFromVoiceConfRequestMessage.EJECT_ALL_VOICE_USERS_REQUEST:
|
||||
processEjectAllVoiceUsersRequestMessage(message);
|
||||
break;
|
||||
case EjectUserFromVoiceConfRequestMessage.EJECT_VOICE_USER_REQUEST:
|
||||
processEjectVoiceUserRequestMessage(message);
|
||||
break;
|
||||
case GetUsersFromVoiceConfRequestMessage.GET_VOICE_USERS_REQUEST:
|
||||
processGetVoiceUsersRequestMessage(message);
|
||||
break;
|
||||
case MuteUserInVoiceConfRequestMessage.MUTE_VOICE_USER_REQUEST:
|
||||
processMuteVoiceUserRequestMessage(message);
|
||||
break;
|
||||
case StartRecordingVoiceConfRequestMessage.START_RECORD_VOICE_CONF_REQUEST:
|
||||
processStartRecordingVoiceConfRequestMessage(message);
|
||||
break;
|
||||
case StopRecordingVoiceConfRequestMessage.STOP_RECORD_VOICE_CONF_REQUEST:
|
||||
processStopRecordingVoiceConfRequestMessage(message);
|
||||
break;
|
||||
case EjectAllUsersFromVoiceConfRequestMessage.EJECT_ALL_VOICE_USERS_REQUEST:
|
||||
processEjectAllVoiceUsersRequestMessage(message);
|
||||
break;
|
||||
case EjectUserFromVoiceConfRequestMessage.EJECT_VOICE_USER_REQUEST:
|
||||
processEjectVoiceUserRequestMessage(message);
|
||||
break;
|
||||
case GetUsersFromVoiceConfRequestMessage.GET_VOICE_USERS_REQUEST:
|
||||
processGetVoiceUsersRequestMessage(message);
|
||||
break;
|
||||
case MuteUserInVoiceConfRequestMessage.MUTE_VOICE_USER_REQUEST:
|
||||
processMuteVoiceUserRequestMessage(message);
|
||||
break;
|
||||
case TransferUserToVoiceConfRequestMessage.TRANSFER_USER_TO_VOICE_CONF_REQUEST:
|
||||
processTransferUserToVoiceConfRequestMessage(message);
|
||||
break;
|
||||
case StartRecordingVoiceConfRequestMessage.START_RECORD_VOICE_CONF_REQUEST:
|
||||
processStartRecordingVoiceConfRequestMessage(message);
|
||||
break;
|
||||
case StopRecordingVoiceConfRequestMessage.STOP_RECORD_VOICE_CONF_REQUEST:
|
||||
processStopRecordingVoiceConfRequestMessage(message);
|
||||
break;
|
||||
case DeskShareStartRTMPBroadcastEventMessage.DESKSHARE_START_RTMP_BROADCAST_MESSAGE:
|
||||
System.out.println("RedisMessageReceiver got DESKSHARE_START_RTMP_BROADCAST_MESSAGE");
|
||||
processDeskShareStartRTMPBroadcastEventMessage(message);
|
||||
@ -90,32 +95,45 @@ public class RedisMessageReceiver {
|
||||
}
|
||||
|
||||
private void processEjectAllVoiceUsersRequestMessage(String json) {
|
||||
EjectAllUsersFromVoiceConfRequestMessage msg = EjectAllUsersFromVoiceConfRequestMessage.fromJson(json);
|
||||
EjectAllUsersFromVoiceConfRequestMessage msg = EjectAllUsersFromVoiceConfRequestMessage
|
||||
.fromJson(json);
|
||||
fsApp.ejectAll(msg.voiceConfId);
|
||||
}
|
||||
|
||||
|
||||
private void processEjectVoiceUserRequestMessage(String json) {
|
||||
EjectUserFromVoiceConfRequestMessage msg = EjectUserFromVoiceConfRequestMessage.fromJson(json);
|
||||
EjectUserFromVoiceConfRequestMessage msg = EjectUserFromVoiceConfRequestMessage
|
||||
.fromJson(json);
|
||||
fsApp.eject(msg.voiceConfId, msg.voiceUserId);
|
||||
}
|
||||
|
||||
|
||||
private void processGetVoiceUsersRequestMessage(String json) {
|
||||
GetUsersFromVoiceConfRequestMessage msg = GetUsersFromVoiceConfRequestMessage.fromJson(json);
|
||||
GetUsersFromVoiceConfRequestMessage msg = GetUsersFromVoiceConfRequestMessage
|
||||
.fromJson(json);
|
||||
fsApp.getAllUsers(msg.voiceConfId);
|
||||
}
|
||||
|
||||
|
||||
private void processMuteVoiceUserRequestMessage(String json) {
|
||||
MuteUserInVoiceConfRequestMessage msg = MuteUserInVoiceConfRequestMessage.fromJson(json);
|
||||
MuteUserInVoiceConfRequestMessage msg = MuteUserInVoiceConfRequestMessage
|
||||
.fromJson(json);
|
||||
fsApp.muteUser(msg.voiceConfId, msg.voiceUserId, msg.mute);
|
||||
}
|
||||
|
||||
|
||||
private void processTransferUserToVoiceConfRequestMessage(String json) {
|
||||
TransferUserToVoiceConfRequestMessage msg = TransferUserToVoiceConfRequestMessage
|
||||
.fromJson(json);
|
||||
fsApp.transferUserToMeeting(msg.voiceConfId, msg.targetVoiceConfId,
|
||||
msg.voiceUserId);
|
||||
}
|
||||
|
||||
private void processStartRecordingVoiceConfRequestMessage(String json) {
|
||||
StartRecordingVoiceConfRequestMessage msg = StartRecordingVoiceConfRequestMessage.fromJson(json);
|
||||
StartRecordingVoiceConfRequestMessage msg = StartRecordingVoiceConfRequestMessage
|
||||
.fromJson(json);
|
||||
fsApp.startRecording(msg.voiceConfId, msg.meetingId);
|
||||
}
|
||||
|
||||
|
||||
private void processStopRecordingVoiceConfRequestMessage(String json) {
|
||||
StopRecordingVoiceConfRequestMessage msg = StopRecordingVoiceConfRequestMessage.fromJson(json);
|
||||
StopRecordingVoiceConfRequestMessage msg = StopRecordingVoiceConfRequestMessage
|
||||
.fromJson(json);
|
||||
fsApp.stopRecording(msg.voiceConfId, msg.meetingId, msg.recordStream);
|
||||
}
|
||||
}
|
||||
|
@ -1,142 +1,160 @@
|
||||
/**
|
||||
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
|
||||
*
|
||||
* Copyright (c) 2012 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) 2012 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.freeswitch.voice.freeswitch;
|
||||
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.bigbluebutton.freeswitch.voice.events.ConferenceEventListener;
|
||||
import org.bigbluebutton.freeswitch.voice.freeswitch.actions.BroadcastConferenceCommand;
|
||||
import org.bigbluebutton.freeswitch.voice.freeswitch.actions.EjectAllUsersCommand;
|
||||
import org.bigbluebutton.freeswitch.voice.freeswitch.actions.EjectUserCommand;
|
||||
import org.bigbluebutton.freeswitch.voice.freeswitch.actions.GetAllUsersCommand;
|
||||
import org.bigbluebutton.freeswitch.voice.freeswitch.actions.MuteUserCommand;
|
||||
import org.bigbluebutton.freeswitch.voice.freeswitch.actions.RecordConferenceCommand;
|
||||
import org.bigbluebutton.freeswitch.voice.freeswitch.actions.TransferUsetToMeetingCommand;
|
||||
import org.bigbluebutton.freeswitch.voice.freeswitch.actions.*;
|
||||
import org.freeswitch.esl.client.inbound.Client;
|
||||
import org.freeswitch.esl.client.inbound.InboundConnectionFailure;
|
||||
import org.freeswitch.esl.client.manager.ManagerConnection;
|
||||
import org.freeswitch.esl.client.transport.message.EslMessage;
|
||||
|
||||
public class ConnectionManager {
|
||||
public class ConnectionManager {
|
||||
|
||||
private static final String EVENT_NAME = "Event-Name";
|
||||
|
||||
private static final ScheduledExecutorService connExec = Executors.newSingleThreadScheduledExecutor();
|
||||
|
||||
private final ManagerConnection manager;
|
||||
private ScheduledFuture<ConnectThread> connectTask;
|
||||
|
||||
private volatile boolean subscribed = false;
|
||||
|
||||
private final ConferenceEventListener conferenceEventListener;
|
||||
private final ESLEventListener eslEventListener;
|
||||
|
||||
public ConnectionManager(ManagerConnection connManager, ESLEventListener eventListener, ConferenceEventListener confListener) {
|
||||
this.manager = connManager;
|
||||
this.eslEventListener = eventListener;
|
||||
this.conferenceEventListener = confListener;
|
||||
}
|
||||
|
||||
private void connect() {
|
||||
try {
|
||||
Client c = manager.getESLClient();
|
||||
if (! c.canSend()) {
|
||||
private static final String EVENT_NAME = "Event-Name";
|
||||
|
||||
private static final ScheduledExecutorService connExec = Executors
|
||||
.newSingleThreadScheduledExecutor();
|
||||
|
||||
private final ManagerConnection manager;
|
||||
private ScheduledFuture<ConnectThread> connectTask;
|
||||
|
||||
private volatile boolean subscribed = false;
|
||||
|
||||
private final ConferenceEventListener conferenceEventListener;
|
||||
private final ESLEventListener eslEventListener;
|
||||
|
||||
public ConnectionManager(ManagerConnection connManager,
|
||||
ESLEventListener eventListener, ConferenceEventListener confListener) {
|
||||
this.manager = connManager;
|
||||
this.eslEventListener = eventListener;
|
||||
this.conferenceEventListener = confListener;
|
||||
}
|
||||
|
||||
private void connect() {
|
||||
try {
|
||||
Client c = manager.getESLClient();
|
||||
if (!c.canSend()) {
|
||||
System.out.println("Attempting to connect to FreeSWITCH ESL");
|
||||
subscribed = false;
|
||||
manager.connect();
|
||||
} else {
|
||||
if (!subscribed) {
|
||||
System.out.println("Subscribing for ESL events.");
|
||||
c.cancelEventSubscriptions();
|
||||
c.addEventListener(eslEventListener);
|
||||
c.setEventSubscriptions( "plain", "all" );
|
||||
c.addEventFilter( EVENT_NAME, "heartbeat" );
|
||||
c.addEventFilter( EVENT_NAME, "custom" );
|
||||
c.addEventFilter( EVENT_NAME, "background_job" );
|
||||
subscribed = true;
|
||||
}
|
||||
}
|
||||
subscribed = false;
|
||||
manager.connect();
|
||||
} else {
|
||||
if (!subscribed) {
|
||||
System.out.println("Subscribing for ESL events.");
|
||||
c.cancelEventSubscriptions();
|
||||
c.addEventListener(eslEventListener);
|
||||
c.setEventSubscriptions("plain", "all");
|
||||
c.addEventFilter(EVENT_NAME, "heartbeat");
|
||||
c.addEventFilter(EVENT_NAME, "custom");
|
||||
c.addEventFilter(EVENT_NAME, "background_job");
|
||||
subscribed = true;
|
||||
}
|
||||
}
|
||||
} catch (InboundConnectionFailure e) {
|
||||
System.out.println("Failed to connect to ESL");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void start() {
|
||||
System.out.println("Starting FreeSWITCH ESL connection manager.");
|
||||
ConnectThread connector = new ConnectThread();
|
||||
connectTask = (ScheduledFuture<ConnectThread>) connExec.scheduleAtFixedRate(connector, 5, 5, TimeUnit.SECONDS);
|
||||
connectTask = (ScheduledFuture<ConnectThread>) connExec
|
||||
.scheduleAtFixedRate(connector, 5, 5, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
|
||||
public void stop() {
|
||||
if (connectTask != null) {
|
||||
connectTask.cancel(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private class ConnectThread implements Runnable {
|
||||
public void run() {
|
||||
connect();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void broadcast(BroadcastConferenceCommand rcc) {
|
||||
Client c = manager.getESLClient();
|
||||
if (c.canSend()) {
|
||||
EslMessage response = c.sendSyncApiCommand(rcc.getCommand(), rcc.getCommandArgs());
|
||||
rcc.handleResponse(response, conferenceEventListener);
|
||||
EslMessage response = c.sendSyncApiCommand(rcc.getCommand(),
|
||||
rcc.getCommandArgs());
|
||||
rcc.handleResponse(response, conferenceEventListener);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void getUsers(GetAllUsersCommand prc) {
|
||||
Client c = manager.getESLClient();
|
||||
if (c.canSend()) {
|
||||
EslMessage response = c.sendSyncApiCommand(prc.getCommand(), prc.getCommandArgs());
|
||||
prc.handleResponse(response, conferenceEventListener);
|
||||
EslMessage response = c.sendSyncApiCommand(prc.getCommand(),
|
||||
prc.getCommandArgs());
|
||||
prc.handleResponse(response, conferenceEventListener);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void mute(MuteUserCommand mpc) {
|
||||
System.out.println("Got mute request from FSApplication.");
|
||||
Client c = manager.getESLClient();
|
||||
if (c.canSend()) {
|
||||
System.out.println("Issuing command to FS ESL.");
|
||||
c.sendAsyncApiCommand( mpc.getCommand(), mpc.getCommandArgs());
|
||||
c.sendAsyncApiCommand(mpc.getCommand(), mpc.getCommandArgs());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void tranfer(TransferUsetToMeetingCommand tutmc) {
|
||||
Client c = manager.getESLClient();
|
||||
if (c.canSend()) {
|
||||
c.sendAsyncApiCommand(tutmc.getCommand(), tutmc.getCommandArgs());
|
||||
}
|
||||
}
|
||||
|
||||
public void eject(EjectUserCommand mpc) {
|
||||
Client c = manager.getESLClient();
|
||||
if (c.canSend()) {
|
||||
c.sendAsyncApiCommand( mpc.getCommand(), mpc.getCommandArgs());
|
||||
}
|
||||
c.sendAsyncApiCommand(mpc.getCommand(), mpc.getCommandArgs());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void ejectAll(EjectAllUsersCommand mpc) {
|
||||
Client c = manager.getESLClient();
|
||||
if (c.canSend()) {
|
||||
c.sendAsyncApiCommand( mpc.getCommand(), mpc.getCommandArgs());
|
||||
c.sendAsyncApiCommand(mpc.getCommand(), mpc.getCommandArgs());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void record(RecordConferenceCommand rcc) {
|
||||
Client c = manager.getESLClient();
|
||||
if (c.canSend()) {
|
||||
EslMessage response = c.sendSyncApiCommand(rcc.getCommand(), rcc.getCommandArgs());
|
||||
rcc.handleResponse(response, conferenceEventListener);
|
||||
EslMessage response = c.sendSyncApiCommand(rcc.getCommand(),
|
||||
rcc.getCommandArgs());
|
||||
rcc.handleResponse(response, conferenceEventListener);
|
||||
}
|
||||
}
|
||||
|
||||
@ -157,4 +175,4 @@ public class ConnectionManager {
|
||||
huCmd.handleResponse(response, conferenceEventListener);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -24,14 +24,23 @@ import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.bigbluebutton.freeswitch.voice.freeswitch.actions.BroadcastConferenceCommand;
|
||||
import org.bigbluebutton.freeswitch.voice.freeswitch.actions.EjectAllUsersCommand;
|
||||
import org.bigbluebutton.freeswitch.voice.freeswitch.actions.EjectUserCommand;
|
||||
import org.bigbluebutton.freeswitch.voice.freeswitch.actions.FreeswitchCommand;
|
||||
import org.bigbluebutton.freeswitch.voice.freeswitch.actions.GetAllUsersCommand;
|
||||
import org.bigbluebutton.freeswitch.voice.freeswitch.actions.MuteUserCommand;
|
||||
import org.bigbluebutton.freeswitch.voice.freeswitch.actions.RecordConferenceCommand;
|
||||
import org.bigbluebutton.freeswitch.voice.freeswitch.actions.TransferUsetToMeetingCommand;
|
||||
import org.bigbluebutton.freeswitch.voice.freeswitch.actions.*;
|
||||
|
||||
public class FreeswitchApplication {
|
||||
|
||||
private static final int SENDERTHREADS = 1;
|
||||
private static final Executor msgSenderExec = Executors.newFixedThreadPool(SENDERTHREADS);
|
||||
private static final Executor runExec = Executors.newFixedThreadPool(SENDERTHREADS);
|
||||
private static final Executor msgSenderExec = Executors
|
||||
.newFixedThreadPool(SENDERTHREADS);
|
||||
private static final Executor runExec = Executors
|
||||
.newFixedThreadPool(SENDERTHREADS);
|
||||
private BlockingQueue<FreeswitchCommand> messages = new LinkedBlockingQueue<FreeswitchCommand>();
|
||||
|
||||
private final ConnectionManager manager;
|
||||
@ -53,6 +62,32 @@ public class FreeswitchApplication {
|
||||
}
|
||||
}
|
||||
|
||||
public void transferUserToMeeting(String voiceConfId,
|
||||
String targetVoiceConfId, String voiceUserId) {
|
||||
TransferUsetToMeetingCommand tutmc = new TransferUsetToMeetingCommand(
|
||||
voiceConfId, targetVoiceConfId, voiceUserId, USER);
|
||||
queueMessage(tutmc);
|
||||
}
|
||||
|
||||
public void start() {
|
||||
sendMessages = true;
|
||||
Runnable sender = new Runnable() {
|
||||
public void run() {
|
||||
while (sendMessages) {
|
||||
FreeswitchCommand message;
|
||||
try {
|
||||
message = messages.take();
|
||||
sendMessageToFreeswitch(message);
|
||||
} catch (InterruptedException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
msgSenderExec.execute(sender);
|
||||
}
|
||||
|
||||
public void getAllUsers(String voiceConfId) {
|
||||
GetAllUsersCommand prc = new GetAllUsersCommand(voiceConfId, USER);
|
||||
queueMessage(prc);
|
||||
@ -134,27 +169,7 @@ public class FreeswitchApplication {
|
||||
runExec.execute(task);
|
||||
}
|
||||
|
||||
public void start() {
|
||||
sendMessages = true;
|
||||
Runnable sender = new Runnable() {
|
||||
public void run() {
|
||||
while (sendMessages) {
|
||||
FreeswitchCommand message;
|
||||
try {
|
||||
message = messages.take();
|
||||
sendMessageToFreeswitch(message);
|
||||
} catch (InterruptedException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
msgSenderExec.execute(sender);
|
||||
public void stop() {
|
||||
sendMessages = false;
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
sendMessages = false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,39 @@
|
||||
/**
|
||||
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
|
||||
*
|
||||
* Copyright (c) 2012 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.freeswitch.voice.freeswitch.actions;
|
||||
|
||||
public class TransferUsetToMeetingCommand extends FreeswitchCommand {
|
||||
|
||||
private final String targetRoom;
|
||||
private final String participant;
|
||||
|
||||
public TransferUsetToMeetingCommand(String room, String targetRoom,
|
||||
String participant, String requesterId) {
|
||||
super(room, requesterId);
|
||||
this.targetRoom = targetRoom;
|
||||
this.participant = participant;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCommandArgs() {
|
||||
return room + SPACE + "transfer" + SPACE + targetRoom + SPACE
|
||||
+ participant;
|
||||
}
|
||||
}
|
@ -4,8 +4,9 @@ akka {
|
||||
receive = on
|
||||
}
|
||||
}
|
||||
loglevel = INFO
|
||||
stdout-loglevel = "INFO"
|
||||
loggers = ["akka.event.slf4j.Slf4jLogger"]
|
||||
loglevel = "DEBUG"
|
||||
stdout-loglevel = "DEBUG"
|
||||
|
||||
rediscala-subscriber-worker-dispatcher {
|
||||
mailbox-type = "akka.dispatch.SingleConsumerOnlyUnboundedMailbox"
|
||||
|
@ -22,7 +22,7 @@
|
||||
<logger name="org.bigbluebutton" level="DEBUG" />
|
||||
<logger name="org.freeswitch.esl" level="WARN" />
|
||||
|
||||
<root level="INFO">
|
||||
<root level="DEBUG">
|
||||
<appender-ref ref="STDOUT"/>
|
||||
<appender-ref ref="FILE" />
|
||||
</root>
|
||||
|
@ -37,4 +37,4 @@ object Boot extends App with SystemConfiguration {
|
||||
val redisMsgReceiver = new RedisMessageReceiver(fsApplication)
|
||||
|
||||
val redisSubscriberActor = system.actorOf(AppsRedisSubscriberActor.props(system, redisMsgReceiver), "redis-subscriber")
|
||||
}
|
||||
}
|
||||
|
@ -196,6 +196,7 @@ public String getJoinURL(String username, String meetingID, String record, Strin
|
||||
String create_parameters = "name=" + urlEncode(meetingID)
|
||||
+ "&meetingID=" + urlEncode(meetingID) + welcome_param + voiceBridge_param
|
||||
+ "&attendeePW=ap&moderatorPW=mp"
|
||||
+ "&isBreakoutRoom=false"
|
||||
+ "&record=" + record + getMetaData( metadata );
|
||||
|
||||
|
||||
|
@ -1,9 +1,8 @@
|
||||
|
||||
name := "bbb-common-message"
|
||||
|
||||
organization := "org.bigbluebutton"
|
||||
|
||||
version := "0.0.17"
|
||||
version := "0.0.18-SNAPSHOT"
|
||||
|
||||
// We want to have our jar files in lib_managed dir.
|
||||
// This way we'll have the right path when we import
|
||||
@ -16,10 +15,10 @@ testOptions in Test += Tests.Argument(TestFrameworks.ScalaTest, "-h", "target/sc
|
||||
|
||||
libraryDependencies ++= {
|
||||
Seq(
|
||||
"com.google.code.gson" % "gson" % "1.7.1"
|
||||
"com.google.code.gson" % "gson" % "2.5"
|
||||
)}
|
||||
|
||||
libraryDependencies += "junit" % "junit" % "4.11" % "test"
|
||||
libraryDependencies += "junit" % "junit" % "4.12" % "test"
|
||||
libraryDependencies += "com.novocode" % "junit-interface" % "0.11" % "test"
|
||||
|
||||
seq(Revolver.settings: _*)
|
||||
@ -47,20 +46,18 @@ autoScalaLibrary := false
|
||||
* publish to the local maven repo using "sbt publish"
|
||||
*/
|
||||
// Uncomment this to publish to local maven repo while commenting out the nexus repo
|
||||
//publishTo := Some(Resolver.file("file", new File(Path.userHome.absolutePath+"/.m2/repository")))
|
||||
publishTo := Some(Resolver.file("file", new File(Path.userHome.absolutePath+"/.m2/repository")))
|
||||
|
||||
|
||||
// Comment this out when publishing to local maven repo using SNAPSHOT version.
|
||||
// To push to sonatype "sbt publishSigned"
|
||||
|
||||
publishTo := {
|
||||
val nexus = "https://oss.sonatype.org/"
|
||||
if (isSnapshot.value)
|
||||
Some("snapshots" at nexus + "content/repositories/snapshots")
|
||||
else
|
||||
Some("releases" at nexus + "service/local/staging/deploy/maven2")
|
||||
}
|
||||
|
||||
//publishTo := {
|
||||
// val nexus = "https://oss.sonatype.org/"
|
||||
// if (isSnapshot.value)
|
||||
// Some("snapshots" at nexus + "content/repositories/snapshots")
|
||||
// else
|
||||
// Some("releases" at nexus + "service/local/staging/deploy/maven2")
|
||||
//}
|
||||
|
||||
// Enables publishing to maven repo
|
||||
publishMavenStyle := true
|
||||
|
@ -125,6 +125,12 @@ public class Constants {
|
||||
public static final String PERM_LOCK_ON_JOIN = "lockOnJoin";
|
||||
public static final String PERM_LOCK_ON_JOIN_CONFIG = "lockOnJoinConfigurable";
|
||||
public static final String ENABLED = "enabled";
|
||||
public static final String START_INDEX = "start_index";
|
||||
public static final String END_INDEX = "end_index";
|
||||
public static final String LOCALE = "locale";
|
||||
public static final String TEXT = "text";
|
||||
public static final String OWNER_ID = "owner_id";
|
||||
public static final String CAPTION_HISTORY = "caption_history";
|
||||
public static final String AVATAR_URL = "avatarURL";
|
||||
public static final String STUNS = "stuns";
|
||||
public static final String TURNS = "turns";
|
||||
|
@ -0,0 +1,74 @@
|
||||
package org.bigbluebutton.common.messages;
|
||||
|
||||
import java.util.HashMap;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
|
||||
|
||||
public class EditCaptionHistoryMessage implements ISubscribedMessage {
|
||||
public static final String EDIT_CAPTION_HISTORY = "edit_caption_history_message";
|
||||
public static final String VERSION = "0.0.1";
|
||||
|
||||
public final String meetingID;
|
||||
public final String userID;
|
||||
public final Integer startIndex;
|
||||
public final Integer endIndex;
|
||||
public final String locale;
|
||||
public final String text;
|
||||
|
||||
public EditCaptionHistoryMessage(String meetingID, String userID, Integer startIndex, Integer endIndex, String locale, String text) {
|
||||
this.meetingID = meetingID;
|
||||
this.userID = userID;
|
||||
this.startIndex = startIndex;
|
||||
this.endIndex = endIndex;
|
||||
this.locale = locale;
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
public String toJson() {
|
||||
HashMap<String, Object> payload = new HashMap<String, Object>();
|
||||
payload.put(Constants.MEETING_ID, meetingID);
|
||||
payload.put(Constants.USER_ID, userID);
|
||||
payload.put(Constants.START_INDEX, startIndex);
|
||||
payload.put(Constants.END_INDEX, endIndex);
|
||||
payload.put(Constants.LOCALE, locale);
|
||||
payload.put(Constants.TEXT, text);
|
||||
|
||||
java.util.HashMap<String, Object> header = MessageBuilder.buildHeader(EDIT_CAPTION_HISTORY, VERSION, null);
|
||||
|
||||
return MessageBuilder.buildJson(header, payload);
|
||||
}
|
||||
|
||||
public static EditCaptionHistoryMessage fromJson(String message) {
|
||||
JsonParser parser = new JsonParser();
|
||||
JsonObject obj = (JsonObject) parser.parse(message);
|
||||
|
||||
if (obj.has("header") && obj.has("payload")) {
|
||||
JsonObject header = (JsonObject) obj.get("header");
|
||||
JsonObject payload = (JsonObject) obj.get("payload");
|
||||
|
||||
if (header.has("name")) {
|
||||
String messageName = header.get("name").getAsString();
|
||||
if (EDIT_CAPTION_HISTORY.equals(messageName)) {
|
||||
if (payload.has(Constants.MEETING_ID)
|
||||
&& payload.has(Constants.USER_ID)
|
||||
&& payload.has(Constants.START_INDEX)
|
||||
&& payload.has(Constants.END_INDEX)
|
||||
&& payload.has(Constants.LOCALE)
|
||||
&& payload.has(Constants.TEXT)) {
|
||||
String meetingID = payload.get(Constants.MEETING_ID).getAsString();
|
||||
String userID = payload.get(Constants.USER_ID).getAsString();
|
||||
Integer startIndex = payload.get(Constants.START_INDEX).getAsInt();
|
||||
Integer endIndex = payload.get(Constants.END_INDEX).getAsInt();
|
||||
String locale = payload.get(Constants.LOCALE).getAsString();
|
||||
String text = payload.get(Constants.TEXT).getAsString();
|
||||
|
||||
return new EditCaptionHistoryMessage(meetingID, userID, startIndex, endIndex, locale, text);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
@ -56,6 +56,5 @@ public class EjectUserFromMeetingRequestMessage implements ISubscribedMessage {
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
package org.bigbluebutton.common.messages;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
|
@ -29,6 +29,7 @@ public class MessagingConstants {
|
||||
public static final String FROM_USERS_CHANNEL = FROM_BBB_APPS_CHANNEL + ":users";
|
||||
public static final String FROM_CHAT_CHANNEL = FROM_BBB_APPS_CHANNEL + ":chat";
|
||||
public static final String FROM_WHITEBOARD_CHANNEL = FROM_BBB_APPS_CHANNEL + ":whiteboard";
|
||||
public static final String FROM_CAPTION_CHANNEL = FROM_BBB_APPS_CHANNEL + ":caption";
|
||||
public static final String FROM_DESK_SHARE_CHANNEL = FROM_BBB_APPS_CHANNEL + ":deskshare";
|
||||
|
||||
public static final String TO_BBB_APPS_CHANNEL = "bigbluebutton:to-bbb-apps";
|
||||
@ -41,6 +42,7 @@ public class MessagingConstants {
|
||||
public static final String TO_CHAT_CHANNEL = TO_BBB_APPS_CHANNEL + ":chat";
|
||||
public static final String TO_VOICE_CHANNEL = TO_BBB_APPS_CHANNEL + ":voice";
|
||||
public static final String TO_WHITEBOARD_CHANNEL = TO_BBB_APPS_CHANNEL + ":whiteboard";
|
||||
public static final String TO_CAPTION_CHANNEL = TO_BBB_APPS_CHANNEL + ":caption";
|
||||
|
||||
public static final String BBB_APPS_KEEP_ALIVE_CHANNEL = "bigbluebutton:from-bbb-apps:keepalive";
|
||||
|
||||
|
@ -63,6 +63,5 @@ public class MuteUserInVoiceConfRequestMessage {
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
package org.bigbluebutton.common.messages;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
|
@ -0,0 +1,64 @@
|
||||
package org.bigbluebutton.common.messages;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
|
||||
public class SendCaptionHistoryReplyMessage implements ISubscribedMessage {
|
||||
public static final String SEND_CAPTION_HISTORY_REPLY = "send_caption_history_reply_message";
|
||||
public static final String VERSION = "0.0.1";
|
||||
|
||||
public final String meetingID;
|
||||
public final String requesterID;
|
||||
public final Map<String, String[]> captionHistory;
|
||||
|
||||
public SendCaptionHistoryReplyMessage(String meetingID, String requesterID, Map<String, String[]> captionHistory) {
|
||||
this.meetingID = meetingID;
|
||||
this.captionHistory = captionHistory;
|
||||
this.requesterID = requesterID;
|
||||
}
|
||||
|
||||
public String toJson() {
|
||||
HashMap<String, Object> payload = new HashMap<String, Object>();
|
||||
payload.put(Constants.MEETING_ID, meetingID);
|
||||
payload.put(Constants.REQUESTER_ID, requesterID);
|
||||
payload.put(Constants.CHAT_HISTORY, captionHistory);
|
||||
|
||||
java.util.HashMap<String, Object> header = MessageBuilder.buildHeader(SEND_CAPTION_HISTORY_REPLY, VERSION, null);
|
||||
return MessageBuilder.buildJson(header, payload);
|
||||
}
|
||||
|
||||
public static SendCaptionHistoryReplyMessage fromJson(String message) {
|
||||
JsonParser parser = new JsonParser();
|
||||
JsonObject obj = (JsonObject) parser.parse(message);
|
||||
if (obj.has("header") && obj.has("payload")) {
|
||||
JsonObject header = (JsonObject) obj.get("header");
|
||||
JsonObject payload = (JsonObject) obj.get("payload");
|
||||
|
||||
if (header.has("name")) {
|
||||
String messageName = header.get("name").getAsString();
|
||||
if (SEND_CAPTION_HISTORY_REPLY.equals(messageName)) {
|
||||
if (payload.has(Constants.MEETING_ID)
|
||||
&& payload.has(Constants.CAPTION_HISTORY)
|
||||
&& payload.has(Constants.REQUESTER_ID)) {
|
||||
String meetingID = payload.get(Constants.MEETING_ID).getAsString();
|
||||
String requesterID = payload.get(Constants.REQUESTER_ID).getAsString();
|
||||
|
||||
JsonObject history = (JsonObject) payload.get(Constants.CAPTION_HISTORY);
|
||||
|
||||
Util util = new Util();
|
||||
|
||||
Map<String, String[]> captionHistory = util.extractCaptionHistory(history);
|
||||
|
||||
return new SendCaptionHistoryReplyMessage(meetingID, requesterID, captionHistory);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
package org.bigbluebutton.common.messages;
|
||||
|
||||
import java.util.HashMap;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
|
||||
|
||||
public class SendCaptionHistoryRequestMessage implements ISubscribedMessage {
|
||||
public static final String SEND_CAPTION_HISTORY_REQUEST = "send_caption_history_request_message";
|
||||
public static final String VERSION = "0.0.1";
|
||||
|
||||
public final String meetingID;
|
||||
public final String requesterID;
|
||||
|
||||
public SendCaptionHistoryRequestMessage(String meetingID, String requesterID) {
|
||||
this.meetingID = meetingID;
|
||||
this.requesterID = requesterID;
|
||||
}
|
||||
|
||||
public String toJson() {
|
||||
HashMap<String, Object> payload = new HashMap<String, Object>();
|
||||
payload.put(Constants.MEETING_ID, meetingID);
|
||||
payload.put(Constants.REQUESTER_ID, requesterID);
|
||||
|
||||
java.util.HashMap<String, Object> header = MessageBuilder.buildHeader(SEND_CAPTION_HISTORY_REQUEST, VERSION, null);
|
||||
|
||||
return MessageBuilder.buildJson(header, payload);
|
||||
}
|
||||
|
||||
public static SendCaptionHistoryRequestMessage fromJson(String message) {
|
||||
JsonParser parser = new JsonParser();
|
||||
JsonObject obj = (JsonObject) parser.parse(message);
|
||||
|
||||
if (obj.has("header") && obj.has("payload")) {
|
||||
JsonObject header = (JsonObject) obj.get("header");
|
||||
JsonObject payload = (JsonObject) obj.get("payload");
|
||||
|
||||
if (header.has("name")) {
|
||||
String messageName = header.get("name").getAsString();
|
||||
if (SEND_CAPTION_HISTORY_REQUEST.equals(messageName)) {
|
||||
if (payload.has(Constants.MEETING_ID)
|
||||
&& payload.has(Constants.REQUESTER_ID)) {
|
||||
String meetingID = payload.get(Constants.MEETING_ID).getAsString();
|
||||
String requesterID = payload.get(Constants.REQUESTER_ID).getAsString();
|
||||
|
||||
return new SendCaptionHistoryRequestMessage(meetingID, requesterID);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
@ -1,10 +1,8 @@
|
||||
package org.bigbluebutton.common.messages;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
|
||||
|
@ -0,0 +1,84 @@
|
||||
/**
|
||||
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
|
||||
*
|
||||
* Copyright (c) 2016 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.common.messages;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
|
||||
public class TransferUserToVoiceConfRequestMessage {
|
||||
public static final String TRANSFER_USER_TO_VOICE_CONF_REQUEST = "transfer_user_to_voice_conf_request_message";
|
||||
public static final String VERSION = "0.0.1";
|
||||
|
||||
public static final String VOICE_CONF_ID = "voice_conf_id";
|
||||
public static final String TARGET_VOICE_CONF_ID = "target_voice_conf_id";
|
||||
public static final String VOICE_USER_ID = "voice_user_id";
|
||||
|
||||
public final String voiceConfId;
|
||||
public final String targetVoiceConfId;
|
||||
public final String voiceUserId;
|
||||
|
||||
public TransferUserToVoiceConfRequestMessage(String voiceConfId,
|
||||
String breakoutVoiceConfId, String voiceUserId) {
|
||||
this.voiceConfId = voiceConfId;
|
||||
this.targetVoiceConfId = breakoutVoiceConfId;
|
||||
this.voiceUserId = voiceUserId;
|
||||
}
|
||||
|
||||
public String toJson() {
|
||||
HashMap<String, Object> payload = new HashMap<String, Object>();
|
||||
payload.put(VOICE_CONF_ID, voiceConfId);
|
||||
payload.put(TARGET_VOICE_CONF_ID, targetVoiceConfId);
|
||||
payload.put(VOICE_USER_ID, voiceUserId);
|
||||
|
||||
java.util.HashMap<String, Object> header = MessageBuilder.buildHeader(
|
||||
TRANSFER_USER_TO_VOICE_CONF_REQUEST, VERSION, null);
|
||||
|
||||
return MessageBuilder.buildJson(header, payload);
|
||||
}
|
||||
|
||||
public static TransferUserToVoiceConfRequestMessage fromJson(String message) {
|
||||
JsonParser parser = new JsonParser();
|
||||
JsonObject obj = (JsonObject) parser.parse(message);
|
||||
|
||||
if (obj.has("header") && obj.has("payload")) {
|
||||
JsonObject header = (JsonObject) obj.get("header");
|
||||
JsonObject payload = (JsonObject) obj.get("payload");
|
||||
|
||||
if (header.has("name")) {
|
||||
String messageName = header.get("name").getAsString();
|
||||
if (TRANSFER_USER_TO_VOICE_CONF_REQUEST.equals(messageName)) {
|
||||
if (payload.has(VOICE_CONF_ID)
|
||||
&& payload.has(TARGET_VOICE_CONF_ID)
|
||||
&& payload.has(VOICE_USER_ID)) {
|
||||
String id = payload.get(VOICE_CONF_ID).getAsString();
|
||||
String targetVoiceConfId = payload.get(
|
||||
TARGET_VOICE_CONF_ID).getAsString();
|
||||
String voiceUserId = payload.get(VOICE_USER_ID)
|
||||
.getAsString();
|
||||
return new TransferUserToVoiceConfRequestMessage(id,
|
||||
targetVoiceConfId, voiceUserId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
package org.bigbluebutton.common.messages;
|
||||
|
||||
import java.util.HashMap;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
|
||||
|
||||
public class UpdateCaptionOwnerMessage implements ISubscribedMessage {
|
||||
public static final String UPDATE_CAPTION_OWNER = "update_caption_owner_message";
|
||||
public static final String VERSION = "0.0.1";
|
||||
|
||||
public final String meetingID;
|
||||
public final String locale;
|
||||
public final String ownerID;
|
||||
|
||||
public UpdateCaptionOwnerMessage(String meetingID, String locale, String ownerID) {
|
||||
this.meetingID = meetingID;
|
||||
this.locale = locale;
|
||||
this.ownerID = ownerID;
|
||||
}
|
||||
|
||||
public String toJson() {
|
||||
HashMap<String, Object> payload = new HashMap<String, Object>();
|
||||
payload.put(Constants.MEETING_ID, meetingID);
|
||||
payload.put(Constants.LOCALE, locale);
|
||||
payload.put(Constants.OWNER_ID, ownerID);
|
||||
|
||||
java.util.HashMap<String, Object> header = MessageBuilder.buildHeader(UPDATE_CAPTION_OWNER, VERSION, null);
|
||||
|
||||
return MessageBuilder.buildJson(header, payload);
|
||||
}
|
||||
|
||||
public static UpdateCaptionOwnerMessage fromJson(String message) {
|
||||
JsonParser parser = new JsonParser();
|
||||
JsonObject obj = (JsonObject) parser.parse(message);
|
||||
|
||||
if (obj.has("header") && obj.has("payload")) {
|
||||
JsonObject header = (JsonObject) obj.get("header");
|
||||
JsonObject payload = (JsonObject) obj.get("payload");
|
||||
|
||||
if (header.has("name")) {
|
||||
String messageName = header.get("name").getAsString();
|
||||
if (UPDATE_CAPTION_OWNER.equals(messageName)) {
|
||||
if (payload.has(Constants.MEETING_ID)
|
||||
&& payload.has(Constants.LOCALE)
|
||||
&& payload.has(Constants.OWNER_ID)) {
|
||||
String meetingID = payload.get(Constants.MEETING_ID).getAsString();
|
||||
String locale = payload.get(Constants.LOCALE).getAsString();
|
||||
String ownerID = payload.get(Constants.OWNER_ID).getAsString();
|
||||
|
||||
return new UpdateCaptionOwnerMessage(meetingID, locale, ownerID);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
@ -717,4 +717,27 @@ public class Util {
|
||||
|
||||
return pollMap;
|
||||
}
|
||||
|
||||
public Map<String, String[]> extractCaptionHistory(JsonObject history) {
|
||||
Map<String, String[]> collection = new HashMap<String, String[]>();
|
||||
|
||||
for (Map.Entry<String,JsonElement> entry : history.entrySet()) {
|
||||
String locale = entry.getKey();
|
||||
JsonArray values = entry.getValue().getAsJsonArray();
|
||||
String[] localeValueArray = new String[2];
|
||||
|
||||
int i = 0;
|
||||
Iterator<JsonElement> valuesIter = values.iterator();
|
||||
while (valuesIter.hasNext()){
|
||||
String element = valuesIter.next().getAsString();
|
||||
|
||||
localeValueArray[i++] = element;
|
||||
}
|
||||
|
||||
collection.put(locale, localeValueArray);
|
||||
}
|
||||
|
||||
return collection;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,42 @@
|
||||
/**
|
||||
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
|
||||
*
|
||||
* Copyright (c) 2015 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.messages;
|
||||
|
||||
import org.bigbluebutton.common.messages.IBigBlueButtonMessage;
|
||||
import org.bigbluebutton.messages.payload.BreakoutRoomPayload;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
|
||||
public class BreakoutRoomClosed implements IBigBlueButtonMessage {
|
||||
public final static String NAME = "BreakoutRoomClosed";
|
||||
|
||||
public final Header header;
|
||||
public final BreakoutRoomPayload payload;
|
||||
|
||||
public BreakoutRoomClosed(BreakoutRoomPayload payload) {
|
||||
header = new Header(NAME);
|
||||
this.payload = payload;
|
||||
}
|
||||
|
||||
public String toJson() {
|
||||
Gson gson = new Gson();
|
||||
return gson.toJson(this);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
/**
|
||||
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
|
||||
*
|
||||
* Copyright (c) 2016 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.messages;
|
||||
|
||||
import org.bigbluebutton.common.messages.IBigBlueButtonMessage;
|
||||
import org.bigbluebutton.messages.payload.BreakoutRoomJoinURLPayload;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
|
||||
public class BreakoutRoomJoinURL implements IBigBlueButtonMessage {
|
||||
public final static String NAME = "BreakoutRoomJoinURL";
|
||||
|
||||
public final Header header;
|
||||
public final BreakoutRoomJoinURLPayload payload;
|
||||
|
||||
public BreakoutRoomJoinURL(BreakoutRoomJoinURLPayload payload) {
|
||||
header = new Header(NAME );
|
||||
this.payload = payload;
|
||||
}
|
||||
|
||||
public String toJson() {
|
||||
Gson gson = new Gson();
|
||||
return gson.toJson(this);
|
||||
}
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package org.bigbluebutton.messages;
|
||||
|
||||
import org.bigbluebutton.common.messages.IBigBlueButtonMessage;
|
||||
import org.bigbluebutton.messages.payload.BreakoutRoomPayload;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
|
||||
public class BreakoutRoomStarted implements IBigBlueButtonMessage {
|
||||
public final static String NAME = "BreakoutRoomStarted";
|
||||
|
||||
public final Header header;
|
||||
public final BreakoutRoomPayload payload;
|
||||
|
||||
public BreakoutRoomStarted(BreakoutRoomPayload payload) {
|
||||
this.header = new Header(NAME);
|
||||
this.payload = payload;
|
||||
}
|
||||
|
||||
public String toJson() {
|
||||
Gson gson = new Gson();
|
||||
return gson.toJson(this);
|
||||
}
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
/**
|
||||
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
|
||||
*
|
||||
* Copyright (c) 2016 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.messages;
|
||||
|
||||
import org.bigbluebutton.common.messages.IBigBlueButtonMessage;
|
||||
import org.bigbluebutton.messages.payload.BreakoutRoomsListPayload;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
|
||||
public class BreakoutRoomsList implements IBigBlueButtonMessage {
|
||||
public final static String NAME = "BreakoutRoomsList";
|
||||
|
||||
public final Header header;
|
||||
public final BreakoutRoomsListPayload payload;
|
||||
|
||||
public BreakoutRoomsList(BreakoutRoomsListPayload payload) {
|
||||
header = new Header(NAME);
|
||||
this.payload = payload;
|
||||
}
|
||||
|
||||
public String toJson() {
|
||||
Gson gson = new Gson();
|
||||
return gson.toJson(this);
|
||||
}
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package org.bigbluebutton.messages;
|
||||
|
||||
import org.bigbluebutton.common.messages.IBigBlueButtonMessage;
|
||||
import org.bigbluebutton.messages.payload.BreakoutRoomsTimeRemainingPayload;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
|
||||
public class BreakoutRoomsTimeRemainingUpdate implements IBigBlueButtonMessage {
|
||||
public final static String NAME = "BreakoutRoomsTimeRemainingUpdate";
|
||||
|
||||
public final Header header;
|
||||
public final BreakoutRoomsTimeRemainingPayload payload;
|
||||
|
||||
public BreakoutRoomsTimeRemainingUpdate(
|
||||
BreakoutRoomsTimeRemainingPayload payload) {
|
||||
header = new Header(NAME);
|
||||
this.payload = payload;
|
||||
}
|
||||
|
||||
public String toJson() {
|
||||
Gson gson = new Gson();
|
||||
return gson.toJson(this);
|
||||
}
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package org.bigbluebutton.messages;
|
||||
|
||||
import org.bigbluebutton.common.messages.IBigBlueButtonMessage;
|
||||
import org.bigbluebutton.messages.payload.CreateBreakoutRoomRequestPayload;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
|
||||
public class CreateBreakoutRoomRequest implements IBigBlueButtonMessage {
|
||||
public final static String NAME = "CreateBreakoutRoomRequest";
|
||||
|
||||
public final Header header;
|
||||
public final CreateBreakoutRoomRequestPayload payload;
|
||||
|
||||
public CreateBreakoutRoomRequest(CreateBreakoutRoomRequestPayload payload) {
|
||||
this.header = new Header(NAME);
|
||||
this.payload = payload;
|
||||
}
|
||||
|
||||
public String toJson() {
|
||||
Gson gson = new Gson();
|
||||
return gson.toJson(this);
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
package org.bigbluebutton.messages;
|
||||
|
||||
import org.bigbluebutton.common.messages.IBigBlueButtonMessage;
|
||||
import org.bigbluebutton.messages.payload.CreateBreakoutRoomsRequestPayload;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
|
||||
/**
|
||||
* The message from the client to the server requesting to create breakout
|
||||
* rooms.
|
||||
*/
|
||||
public class CreateBreakoutRoomsRequest implements IBigBlueButtonMessage {
|
||||
public final static String NAME = "CreateBreakoutRoomsRequest";
|
||||
|
||||
public final Header header;
|
||||
public final CreateBreakoutRoomsRequestPayload payload;
|
||||
|
||||
public CreateBreakoutRoomsRequest(CreateBreakoutRoomsRequestPayload payload) {
|
||||
this.header = new Header(NAME);
|
||||
this.payload = payload;
|
||||
}
|
||||
|
||||
public String toJson() {
|
||||
Gson gson = new Gson();
|
||||
return gson.toJson(this);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
package org.bigbluebutton.messages;
|
||||
|
||||
import org.bigbluebutton.common.messages.IBigBlueButtonMessage;
|
||||
|
||||
public class CreateMeetingRequest implements IBigBlueButtonMessage {
|
||||
public final static String NAME = "CreateMeetingRequest";
|
||||
|
||||
public final Header header;
|
||||
public final CreateMeetingRequestPayload payload;
|
||||
|
||||
public CreateMeetingRequest(CreateMeetingRequestPayload payload) {
|
||||
this.header = new Header(NAME);
|
||||
this.payload = payload;
|
||||
}
|
||||
|
||||
public static class CreateMeetingRequestPayload {
|
||||
public final String id;
|
||||
public final String externalId;
|
||||
public final String name;
|
||||
public final Boolean record;
|
||||
public final String voiceConfId;
|
||||
public final Integer durationInMinutes;
|
||||
public final Boolean autoStartRecording;
|
||||
public final Boolean allowStartStopRecording;
|
||||
public final String moderatorPassword;
|
||||
public final String viewerPassword;
|
||||
public final Long createTime;
|
||||
public final String createDate;
|
||||
public final Boolean isBreakout;
|
||||
|
||||
public CreateMeetingRequestPayload(String id, String externalId, String name, Boolean record, String voiceConfId,
|
||||
Integer duration, Boolean autoStartRecording,
|
||||
Boolean allowStartStopRecording, String moderatorPass,
|
||||
String viewerPass, Long createTime, String createDate, Boolean isBreakout) {
|
||||
this.id = id;
|
||||
this.externalId = externalId;
|
||||
this.name = name;
|
||||
this.record = record;
|
||||
this.voiceConfId = voiceConfId;
|
||||
this.durationInMinutes = duration;
|
||||
this.autoStartRecording = autoStartRecording;
|
||||
this.allowStartStopRecording = allowStartStopRecording;
|
||||
this.moderatorPassword = moderatorPass;
|
||||
this.viewerPassword = viewerPass;
|
||||
this.createTime = createTime;
|
||||
this.createDate = createDate;
|
||||
this.isBreakout = isBreakout;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
/**
|
||||
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
|
||||
*
|
||||
* Copyright (c) 2015 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.messages;
|
||||
|
||||
import org.bigbluebutton.common.messages.IBigBlueButtonMessage;
|
||||
import org.bigbluebutton.messages.payload.EndAllBreakoutRoomsRequestPayload;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
|
||||
public class EndAllBreakoutRoomsRequest implements IBigBlueButtonMessage {
|
||||
public final static String NAME = "EndAllBreakoutRoomsRequest";
|
||||
|
||||
public final Header header;
|
||||
public final EndAllBreakoutRoomsRequestPayload payload;
|
||||
|
||||
public EndAllBreakoutRoomsRequest(EndAllBreakoutRoomsRequestPayload payload) {
|
||||
this.header = new Header(NAME);
|
||||
this.payload = payload;
|
||||
}
|
||||
|
||||
public String toJson() {
|
||||
Gson gson = new Gson();
|
||||
return gson.toJson(this);
|
||||
}
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
/**
|
||||
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
|
||||
*
|
||||
* Copyright (c) 2016 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.messages;
|
||||
|
||||
import org.bigbluebutton.common.messages.IBigBlueButtonMessage;
|
||||
import org.bigbluebutton.messages.payload.EndBreakoutRoomRequestPayload;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
|
||||
public class EndBreakoutRoomRequest implements IBigBlueButtonMessage {
|
||||
public final static String NAME = "EndBreakoutRoomRequest";
|
||||
|
||||
public final Header header;
|
||||
public final EndBreakoutRoomRequestPayload payload;
|
||||
|
||||
public EndBreakoutRoomRequest(EndBreakoutRoomRequestPayload payload) {
|
||||
header = new Header(NAME);
|
||||
this.payload = payload;
|
||||
}
|
||||
|
||||
public String toJson() {
|
||||
Gson gson = new Gson();
|
||||
return gson.toJson(this);
|
||||
}
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
/**
|
||||
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
|
||||
*
|
||||
* Copyright (c) 2016 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.messages;
|
||||
|
||||
import org.bigbluebutton.common.messages.IBigBlueButtonMessage;
|
||||
import org.bigbluebutton.messages.payload.GetBreakoutRoomsListPayload;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
|
||||
public class GetBreakoutRoomsList implements IBigBlueButtonMessage {
|
||||
public final static String NAME = "GetBreakoutRoomsList";
|
||||
|
||||
public final Header header;
|
||||
public final GetBreakoutRoomsListPayload payload;
|
||||
|
||||
public GetBreakoutRoomsList(GetBreakoutRoomsListPayload payload) {
|
||||
header = new Header(NAME);
|
||||
this.payload = payload;
|
||||
}
|
||||
|
||||
public String toJson() {
|
||||
Gson gson = new Gson();
|
||||
return gson.toJson(this);
|
||||
}
|
||||
}
|
14
bbb-common-message/src/main/java/org/bigbluebutton/messages/Header.java
Executable file
14
bbb-common-message/src/main/java/org/bigbluebutton/messages/Header.java
Executable file
@ -0,0 +1,14 @@
|
||||
package org.bigbluebutton.messages;
|
||||
|
||||
|
||||
public class Header {
|
||||
public enum MessageType {
|
||||
SYSTEM, BROADCAST, DIRECT
|
||||
}
|
||||
|
||||
public final String name;
|
||||
|
||||
public Header(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package org.bigbluebutton.messages;
|
||||
|
||||
import org.bigbluebutton.common.messages.IBigBlueButtonMessage;
|
||||
import org.bigbluebutton.messages.payload.ListenInOnBreakoutPayload;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
|
||||
public class ListenInOnBreakout implements IBigBlueButtonMessage {
|
||||
public final static String NAME = "ListenInOnBreakout";
|
||||
|
||||
public final Header header;
|
||||
public final ListenInOnBreakoutPayload payload;
|
||||
|
||||
public ListenInOnBreakout(ListenInOnBreakoutPayload payload) {
|
||||
this.header = new Header(NAME);
|
||||
this.payload = payload;
|
||||
}
|
||||
|
||||
public String toJson() {
|
||||
Gson gson = new Gson();
|
||||
return gson.toJson(this);
|
||||
}
|
||||
}
|
14
bbb-common-message/src/main/java/org/bigbluebutton/messages/Message.java
Executable file
14
bbb-common-message/src/main/java/org/bigbluebutton/messages/Message.java
Executable file
@ -0,0 +1,14 @@
|
||||
package org.bigbluebutton.messages;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
public class Message {
|
||||
|
||||
public final Header header;
|
||||
public final JsonObject body;
|
||||
|
||||
public Message(Header header, JsonObject body) {
|
||||
this.header = header;
|
||||
this.body = body;
|
||||
}
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
package org.bigbluebutton.messages;
|
||||
|
||||
public enum MessageType {
|
||||
SYSTEM, DIRECT, BROADCAST
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user