|
@@ -17,6 +17,7 @@ import initStropheLogger from './strophe.logger';
|
17
|
17
|
import Listenable from '../util/Listenable';
|
18
|
18
|
import Caps from './Caps';
|
19
|
19
|
import GlobalOnErrorHandler from '../util/GlobalOnErrorHandler';
|
|
20
|
+import XMPPEvents from '../../service/xmpp/XMPPEvents';
|
20
|
21
|
|
21
|
22
|
const logger = getLogger(__filename);
|
22
|
23
|
|
|
@@ -40,6 +41,14 @@ function createConnection(token, bosh = '/http-bind') {
|
40
|
41
|
return conn;
|
41
|
42
|
}
|
42
|
43
|
|
|
44
|
+/**
|
|
45
|
+ * The name of the field used to recognize a chat message as carrying a JSON
|
|
46
|
+ * payload from another endpoint.
|
|
47
|
+ * If the json-message of a chat message contains a valid JSON object, and
|
|
48
|
+ * the JSON has this key, then it is a valid json-message to be sent.
|
|
49
|
+ */
|
|
50
|
+export const JITSI_MEET_MUC_TYPE = 'type';
|
|
51
|
+
|
43
|
52
|
/**
|
44
|
53
|
*
|
45
|
54
|
*/
|
|
@@ -167,6 +176,10 @@ export default class XMPP extends Listenable {
|
167
|
176
|
identities.forEach(identity => {
|
168
|
177
|
if (identity.type === 'speakerstats') {
|
169
|
178
|
this.speakerStatsComponentAddress = identity.name;
|
|
179
|
+
|
|
180
|
+ this.connection.addHandler(
|
|
181
|
+ this._onPrivateMessage.bind(this), null,
|
|
182
|
+ 'message', null, null);
|
170
|
183
|
}
|
171
|
184
|
});
|
172
|
185
|
})
|
|
@@ -588,4 +601,70 @@ export default class XMPP extends Listenable {
|
588
|
601
|
|
589
|
602
|
this.connection.send(msg);
|
590
|
603
|
}
|
|
604
|
+
|
|
605
|
+ /**
|
|
606
|
+ * Check if the given argument is a valid JSON ENDPOINT_MESSAGE string by
|
|
607
|
+ * parsing it and checking if it has a field called 'type'.
|
|
608
|
+ *
|
|
609
|
+ * @param {string} jsonString check if this string is a valid json string
|
|
610
|
+ * and contains the special structure.
|
|
611
|
+ * @returns {boolean, object} if given object is a valid JSON string, return
|
|
612
|
+ * the json object. Otherwise, returns false.
|
|
613
|
+ */
|
|
614
|
+ tryParseJSONAndVerify(jsonString) {
|
|
615
|
+ try {
|
|
616
|
+ const json = JSON.parse(jsonString);
|
|
617
|
+
|
|
618
|
+ // Handle non-exception-throwing cases:
|
|
619
|
+ // Neither JSON.parse(false) or JSON.parse(1234) throw errors,
|
|
620
|
+ // hence the type-checking,
|
|
621
|
+ // but... JSON.parse(null) returns null, and
|
|
622
|
+ // typeof null === "object",
|
|
623
|
+ // so we must check for that, too.
|
|
624
|
+ // Thankfully, null is falsey, so this suffices:
|
|
625
|
+ if (json && typeof json === 'object') {
|
|
626
|
+ const type = json[JITSI_MEET_MUC_TYPE];
|
|
627
|
+
|
|
628
|
+ if (typeof type !== 'undefined') {
|
|
629
|
+ return json;
|
|
630
|
+ }
|
|
631
|
+
|
|
632
|
+ logger.debug('parsing valid json but does not have correct '
|
|
633
|
+ + 'structure', 'topic: ', type);
|
|
634
|
+ }
|
|
635
|
+ } catch (e) {
|
|
636
|
+ return false;
|
|
637
|
+ }
|
|
638
|
+
|
|
639
|
+ return false;
|
|
640
|
+ }
|
|
641
|
+
|
|
642
|
+ /**
|
|
643
|
+ * A private message is received, message that is not addressed to the muc.
|
|
644
|
+ * We expect private message coming from speaker stats component if it is
|
|
645
|
+ * enabled and running.
|
|
646
|
+ *
|
|
647
|
+ * @param {string} msg - The message.
|
|
648
|
+ */
|
|
649
|
+ _onPrivateMessage(msg) {
|
|
650
|
+ const from = msg.getAttribute('from');
|
|
651
|
+
|
|
652
|
+ if (!this.speakerStatsComponentAddress
|
|
653
|
+ || from !== this.speakerStatsComponentAddress) {
|
|
654
|
+ return;
|
|
655
|
+ }
|
|
656
|
+
|
|
657
|
+ const jsonMessage = $(msg).find('>json-message')
|
|
658
|
+ .text();
|
|
659
|
+ const parsedJson = this.tryParseJSONAndVerify(jsonMessage);
|
|
660
|
+
|
|
661
|
+ if (parsedJson
|
|
662
|
+ && parsedJson[JITSI_MEET_MUC_TYPE] === 'speakerstats'
|
|
663
|
+ && parsedJson.users) {
|
|
664
|
+ this.eventEmitter.emit(
|
|
665
|
+ XMPPEvents.SPEAKER_STATS_RECEIVED, parsedJson.users);
|
|
666
|
+ }
|
|
667
|
+
|
|
668
|
+ return true;
|
|
669
|
+ }
|
591
|
670
|
}
|