瀏覽代碼

Handles private messages received by speakerstats component.

Updates speakers stats with values received by the spealerstats component, all the logic is activate only in case we discover speakerstats component address from disco-info and the incoming message is coming from that component.
dev1
damencho 7 年之前
父節點
當前提交
7f52a2cc60
共有 5 個文件被更改,包括 119 次插入48 次删除
  1. 1
    1
      JitsiConference.js
  2. 33
    0
      modules/statistics/SpeakerStatsCollector.js
  3. 1
    47
      modules/xmpp/ChatRoom.js
  4. 79
    0
      modules/xmpp/xmpp.js
  5. 5
    0
      service/xmpp/XMPPEvents.js

+ 1
- 1
JitsiConference.js 查看文件

33
 import ComponentsVersions from './modules/version/ComponentsVersions';
33
 import ComponentsVersions from './modules/version/ComponentsVersions';
34
 import VideoSIPGW from './modules/videosipgw/VideoSIPGW';
34
 import VideoSIPGW from './modules/videosipgw/VideoSIPGW';
35
 import * as VideoSIPGWConstants from './modules/videosipgw/VideoSIPGWConstants';
35
 import * as VideoSIPGWConstants from './modules/videosipgw/VideoSIPGWConstants';
36
-import { JITSI_MEET_MUC_TYPE } from './modules/xmpp/ChatRoom';
36
+import { JITSI_MEET_MUC_TYPE } from './modules/xmpp/xmpp';
37
 import * as MediaType from './service/RTC/MediaType';
37
 import * as MediaType from './service/RTC/MediaType';
38
 import * as RTCEvents from './service/RTC/RTCEvents';
38
 import * as RTCEvents from './service/RTC/RTCEvents';
39
 import VideoType from './service/RTC/VideoType';
39
 import VideoType from './service/RTC/VideoType';

+ 33
- 0
modules/statistics/SpeakerStatsCollector.js 查看文件

1
 import * as JitsiConferenceEvents from '../../JitsiConferenceEvents';
1
 import * as JitsiConferenceEvents from '../../JitsiConferenceEvents';
2
 import SpeakerStats from './SpeakerStats';
2
 import SpeakerStats from './SpeakerStats';
3
+import XMPPEvents from '../../service/xmpp/XMPPEvents';
3
 
4
 
4
 /**
5
 /**
5
  * A collection for tracking speaker stats. Attaches listeners
6
  * A collection for tracking speaker stats. Attaches listeners
38
         conference.addEventListener(
39
         conference.addEventListener(
39
             JitsiConferenceEvents.DISPLAY_NAME_CHANGED,
40
             JitsiConferenceEvents.DISPLAY_NAME_CHANGED,
40
             this._onDisplayNameChange.bind(this));
41
             this._onDisplayNameChange.bind(this));
42
+        conference.xmpp.addListener(
43
+            XMPPEvents.SPEAKER_STATS_RECEIVED,
44
+            this._updateStats.bind(this));
41
     }
45
     }
42
 
46
 
43
     /**
47
     /**
118
     getStats() {
122
     getStats() {
119
         return this.stats.users;
123
         return this.stats.users;
120
     }
124
     }
125
+
126
+    /**
127
+     * Updates of the current stats is requested, passing the new values.
128
+     *
129
+     * @param {Object} newStats - The new values used to update current one.
130
+     * @private
131
+     */
132
+    _updateStats(newStats) {
133
+        for (const userId in newStats) { // eslint-disable-line guard-for-in
134
+            let speakerStatsToUpdate;
135
+
136
+            if (this.stats.users[userId]) {
137
+                speakerStatsToUpdate = this.stats.users[userId];
138
+
139
+                if (!speakerStatsToUpdate.getDisplayName()) {
140
+                    speakerStatsToUpdate
141
+                        .setDisplayName(newStats[userId].displayName);
142
+                }
143
+            } else {
144
+                speakerStatsToUpdate = new SpeakerStats(
145
+                    userId, newStats[userId].displayName);
146
+                this.stats.users[userId] = speakerStatsToUpdate;
147
+                speakerStatsToUpdate.markAsHasLeft();
148
+            }
149
+
150
+            speakerStatsToUpdate.totalDominantSpeakerTime
151
+                = newStats[userId].totalDominantSpeakerTime;
152
+        }
153
+    }
121
 }
154
 }

+ 1
- 47
modules/xmpp/ChatRoom.js 查看文件

76
     return res;
76
     return res;
77
 }
77
 }
78
 
78
 
79
-/**
80
- * The name of the field used to recognize a chat message as carrying a JSON
81
- * payload from another endpoint.
82
- * If the json-message of a chat message contains a valid JSON object, and the
83
- * JSON has this key, then it is a valid json-message to be sent.
84
- */
85
-export const JITSI_MEET_MUC_TYPE = 'type';
86
-
87
-/**
88
- * Check if the given argument is a valid JSON ENDPOINT_MESSAGE string by
89
- * parsing it and checking if it has a field called 'type'.
90
- *
91
- * @param {string} jsonString check if this string is a valid json string
92
- * and contains the special structure.
93
- * @returns {boolean, object} if given object is a valid JSON string, return
94
- * the json object. Otherwise, returns false.
95
- */
96
-function tryParseJSONAndVerify(jsonString) {
97
-    try {
98
-        const json = JSON.parse(jsonString);
99
-
100
-        // Handle non-exception-throwing cases:
101
-        // Neither JSON.parse(false) or JSON.parse(1234) throw errors,
102
-        // hence the type-checking,
103
-        // but... JSON.parse(null) returns null, and
104
-        // typeof null === "object",
105
-        // so we must check for that, too.
106
-        // Thankfully, null is falsey, so this suffices:
107
-        if (json && typeof json === 'object') {
108
-            const type = json[JITSI_MEET_MUC_TYPE];
109
-
110
-            if (typeof type !== 'undefined') {
111
-                return json;
112
-            }
113
-
114
-            logger.debug('parsing valid json but does not have correct '
115
-                + 'structure', 'topic: ', type);
116
-        }
117
-    } catch (e) {
118
-
119
-        return false;
120
-    }
121
-
122
-    return false;
123
-}
124
-
125
 // XXX As ChatRoom constructs XMPP stanzas and Strophe is build around the idea
79
 // XXX As ChatRoom constructs XMPP stanzas and Strophe is build around the idea
126
 // of chaining function calls, allow long function call chains.
80
 // of chaining function calls, allow long function call chains.
127
 /* eslint-disable newline-per-chained-call */
81
 /* eslint-disable newline-per-chained-call */
925
             this.discoRoomInfo();
879
             this.discoRoomInfo();
926
         }
880
         }
927
         const jsonMessage = $(msg).find('>json-message').text();
881
         const jsonMessage = $(msg).find('>json-message').text();
928
-        const parsedJson = tryParseJSONAndVerify(jsonMessage);
882
+        const parsedJson = this.xmpp.tryParseJSONAndVerify(jsonMessage);
929
 
883
 
930
         // We emit this event if the message is a valid json, and is not
884
         // We emit this event if the message is a valid json, and is not
931
         // delivered after a delay, i.e. stamp is undefined.
885
         // delivered after a delay, i.e. stamp is undefined.

+ 79
- 0
modules/xmpp/xmpp.js 查看文件

17
 import Listenable from '../util/Listenable';
17
 import Listenable from '../util/Listenable';
18
 import Caps from './Caps';
18
 import Caps from './Caps';
19
 import GlobalOnErrorHandler from '../util/GlobalOnErrorHandler';
19
 import GlobalOnErrorHandler from '../util/GlobalOnErrorHandler';
20
+import XMPPEvents from '../../service/xmpp/XMPPEvents';
20
 
21
 
21
 const logger = getLogger(__filename);
22
 const logger = getLogger(__filename);
22
 
23
 
40
     return conn;
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
                     identities.forEach(identity => {
176
                     identities.forEach(identity => {
168
                         if (identity.type === 'speakerstats') {
177
                         if (identity.type === 'speakerstats') {
169
                             this.speakerStatsComponentAddress = identity.name;
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
 
601
 
589
         this.connection.send(msg);
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
 }

+ 5
- 0
service/xmpp/XMPPEvents.js 查看文件

199
      */
199
      */
200
     SESSION_ACCEPT_TIMEOUT: 'xmpp.session_accept_timeout',
200
     SESSION_ACCEPT_TIMEOUT: 'xmpp.session_accept_timeout',
201
 
201
 
202
+    /**
203
+     * Event fired when speaker stats update message is received.
204
+     */
205
+    SPEAKER_STATS_RECEIVED: 'xmpp.speaker_stats_received',
206
+
202
     // Designates an event indicating that we should join the conference with
207
     // Designates an event indicating that we should join the conference with
203
     // audio and/or video muted.
208
     // audio and/or video muted.
204
     START_MUTED_FROM_FOCUS: 'xmpp.start_muted_from_focus',
209
     START_MUTED_FROM_FOCUS: 'xmpp.start_muted_from_focus',

Loading…
取消
儲存