|
@@ -1,838 +0,0 @@
|
|
|
-(function (root, factory) {
|
|
|
- if (typeof define === 'function' && define.amd) {
|
|
|
- define(["converse"], factory);
|
|
|
- } else {
|
|
|
- factory(converse);
|
|
|
- }
|
|
|
-}(this, function (converse) {
|
|
|
- let _converse, Strophe, $iq, $msg, $pres, $build, b64_sha1, _ , dayjs, html;
|
|
|
- let ohun = {}, ohunRoom, configuration;
|
|
|
-
|
|
|
- window.addEventListener("unload", function()
|
|
|
- {
|
|
|
- const peers = Object.getOwnPropertyNames(ohun)
|
|
|
-
|
|
|
- for (let i=0; i<peers.length; i++)
|
|
|
- {
|
|
|
- if (ohun[peers[i]].peer) ohun[peers[i]].peer.close();
|
|
|
- if (ohun[peers[i]].localStream) ohun[peers[i]].localStream.getTracks().forEach(track => track.stop());
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- converse.plugins.add("voice-chat", {
|
|
|
- dependencies: [],
|
|
|
-
|
|
|
- initialize: function () {
|
|
|
- _converse = this._converse;
|
|
|
- Strophe = converse.env.Strophe;
|
|
|
- $iq = converse.env.$iq;
|
|
|
- $msg = converse.env.$msg;
|
|
|
- $pres = converse.env.$pres;
|
|
|
- $build = converse.env.$build;
|
|
|
- b64_sha1 = converse.env.b64_sha1;
|
|
|
- _ = converse.env._;
|
|
|
- dayjs = converse.env.dayjs;
|
|
|
-
|
|
|
- _converse.api.settings.set("visible_toolbar_buttons", {call: true});
|
|
|
-
|
|
|
- _converse.api.listen.on('connected', function()
|
|
|
- {
|
|
|
- listenForOhunEvents();
|
|
|
- });
|
|
|
-
|
|
|
- _converse.api.listen.on('getToolbarButtons', function(toolbar_el, buttons)
|
|
|
- {
|
|
|
- const jid = toolbar_el.model.get("jid");
|
|
|
-
|
|
|
- if (jid.endsWith(_converse.connection.domain))
|
|
|
- {
|
|
|
- setTimeout(function()
|
|
|
- {
|
|
|
- const icon = toolbar_el.querySelector("button.toggle-call converse-icon.fa.fa-phone");
|
|
|
- ohun[jid] = {icon: icon};
|
|
|
- console.debug("getToolbarButtons", ohun);
|
|
|
-
|
|
|
- }, 3000);
|
|
|
- }
|
|
|
-
|
|
|
- return buttons;
|
|
|
- });
|
|
|
-
|
|
|
- _converse.api.listen.on('callButtonClicked', function(data)
|
|
|
- {
|
|
|
- const jid = data.model.get("jid");
|
|
|
- const view = _converse.chatboxviews.get(jid);
|
|
|
-
|
|
|
- console.debug("callButtonClicked", jid, view);
|
|
|
-
|
|
|
- if (jid.endsWith(_converse.connection.domain))
|
|
|
- {
|
|
|
- if (configuration && view)
|
|
|
- {
|
|
|
- ohun[jid].view = view;
|
|
|
- const icon = view.el.querySelector("button.toggle-call converse-icon.fa.fa-phone svg");
|
|
|
- icon.setAttribute("data-status", "off");
|
|
|
- startVoiceChat(view.model, icon);
|
|
|
- }
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- _converse.api.listen.on('chatBoxClosed', function (view)
|
|
|
- {
|
|
|
- const jid = view.model.get("jid");
|
|
|
- console.debug("chatBoxClosed", jid);
|
|
|
- disconnectKraken(view.model, true);
|
|
|
- });
|
|
|
-
|
|
|
- _converse.api.listen.on('parseMessage', parseVoiceChatMessage);
|
|
|
- _converse.api.listen.on('parseMUCMessage', parseVoiceChatMessage);
|
|
|
-
|
|
|
- console.debug("voice-chat plugin is ready");
|
|
|
- },
|
|
|
-
|
|
|
- overrides: {
|
|
|
- MessageView: {
|
|
|
-
|
|
|
- renderChatMessage: async function renderChatMessage()
|
|
|
- {
|
|
|
- if (this.model.get("json_type")) {
|
|
|
- const json = this.model.get("json_payload");
|
|
|
- console.debug('ohun - renderChatMessage', this.model.get("json_jid"), json);
|
|
|
-
|
|
|
- if (json.method == "publish" || json.method == "end")
|
|
|
- {
|
|
|
- this.model.set('message', '/me ' + (json.method == 'publish' ? 'starts' : 'stops') + ' ohun');
|
|
|
- await this.__super__.renderChatMessage.apply(this, arguments);
|
|
|
- }
|
|
|
- } else {
|
|
|
- await this.__super__.renderChatMessage.apply(this, arguments);
|
|
|
- }
|
|
|
- }
|
|
|
- },
|
|
|
-
|
|
|
- ChatBoxView: {
|
|
|
-
|
|
|
- parseMessageForCommands: function(text)
|
|
|
- {
|
|
|
- console.debug('ohun - parseMessageForCommands', text);
|
|
|
-
|
|
|
- const match = text.replace(/^\s*/, "").match(/^\/(.*?)(?: (.*))?$/) || [false, '', ''];
|
|
|
- const command = match[1].toLowerCase();
|
|
|
- const view = this;
|
|
|
-
|
|
|
- let nick = match[2];
|
|
|
-
|
|
|
- if (_converse.connection.pass && _converse.connection.pass != "" && (command === "vchat"))
|
|
|
- {
|
|
|
- let jid = view.model.get("jid");
|
|
|
-
|
|
|
- if (ohun[jid].localStream)
|
|
|
- {
|
|
|
- if (this.model.get("type") == "chatroom")
|
|
|
- {
|
|
|
- if (!nick)
|
|
|
- {
|
|
|
- this.showHelpMessages(["Nickname required"]);
|
|
|
- return true;
|
|
|
- }
|
|
|
-
|
|
|
- nick = nick.trim();
|
|
|
- if (nick.startsWith("@")) nick = nick.substring(1);
|
|
|
- }
|
|
|
-
|
|
|
- const occupant = this.model.occupants.findWhere({'nick': nick});
|
|
|
-
|
|
|
- if (occupant) {
|
|
|
- handleUserClick(jid, occupant.get('jid'));
|
|
|
- } else {
|
|
|
- this.showHelpMessages(["Nickname not found"]);
|
|
|
- }
|
|
|
- } else {
|
|
|
- this.showHelpMessages(["Ohun not yet started"]);
|
|
|
- }
|
|
|
- return true;
|
|
|
- }
|
|
|
-
|
|
|
- return false;
|
|
|
- }
|
|
|
- }
|
|
|
- },
|
|
|
-
|
|
|
- closeKraken: function(chat)
|
|
|
- {
|
|
|
- console.debug("closeKraken", chat);
|
|
|
- disconnectKraken(chat, true);
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- function parseVoiceChatMessage(stanza, attrs)
|
|
|
- {
|
|
|
- const json = stanza.querySelector('json');
|
|
|
-
|
|
|
- if (json)
|
|
|
- {
|
|
|
- console.debug("parseVoiceChatMessage", json);
|
|
|
-
|
|
|
- attrs['json_jid'] = json.getAttribute('jid');
|
|
|
- attrs['json_type'] = json.getAttribute('type');
|
|
|
- attrs['json_payload'] = json.innerHTML;
|
|
|
- }
|
|
|
-
|
|
|
- return attrs
|
|
|
- }
|
|
|
-
|
|
|
- function listenForOhunEvents()
|
|
|
- {
|
|
|
- console.debug("listenForOhunEvents");
|
|
|
-
|
|
|
- getStunTurn();
|
|
|
- addXmppEventHandler();
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- function addXmppEventHandler()
|
|
|
- {
|
|
|
- _converse.connection.addHandler(function (message)
|
|
|
- {
|
|
|
- const to = Strophe.getBareJidFromJid(message.getAttribute("to"));
|
|
|
- const from = Strophe.getBareJidFromJid(message.getAttribute("from"));
|
|
|
- const json_ele = message.querySelector("json");
|
|
|
- const json = JSON.parse(json_ele.innerHTML);
|
|
|
-
|
|
|
- ohunRoom = decodeURIComponent(json.id);
|
|
|
-
|
|
|
- const room = ohunRoom;
|
|
|
- const json_jid = Strophe.getBareJidFromJid(json_ele.getAttribute("jid"));
|
|
|
- const json_type = json_ele.getAttribute("type");
|
|
|
-
|
|
|
- async function handlePeerEnd(json)
|
|
|
- {
|
|
|
- console.debug("handlePeerEnd", from, json);
|
|
|
- const chat = _converse.chatboxes.get(room);
|
|
|
- disconnectKraken(chat, false);
|
|
|
- }
|
|
|
-
|
|
|
- async function handlePeerTrickle(json)
|
|
|
- {
|
|
|
- console.debug("handlePeerTrickle", from, json);
|
|
|
-
|
|
|
- const data = JSON.parse(json.params[3]);
|
|
|
- const candidate = new RTCIceCandidate(data);
|
|
|
- await ohun[room].peer.addIceCandidate(candidate);
|
|
|
- }
|
|
|
-
|
|
|
- async function handlePeerAnswer(json)
|
|
|
- {
|
|
|
- console.debug("handlePeerAnswer", from, json);
|
|
|
-
|
|
|
- ohun[room].ucid = json.id;
|
|
|
- const data = JSON.parse(json.params[2]);
|
|
|
- await ohun[room].peer.setRemoteDescription(new RTCSessionDescription(data));
|
|
|
- sendCandidates();
|
|
|
- }
|
|
|
-
|
|
|
- async function handlePeerPublish(json)
|
|
|
- {
|
|
|
- console.debug("handlePublish", from, json);
|
|
|
-
|
|
|
- var prompt = new Notification(from, {
|
|
|
- 'body': "Voice Chat?",
|
|
|
- 'lang': _converse.locale,
|
|
|
- 'icon': _converse.notification_icon,
|
|
|
- 'requireInteraction': true
|
|
|
- });
|
|
|
-
|
|
|
- prompt.onclick = function(event)
|
|
|
- {
|
|
|
- //event.preventDefault();
|
|
|
-
|
|
|
- _converse.api.chats.open(from, {'bring_to_foreground': true}, true).then(chat =>
|
|
|
- {
|
|
|
- const room = chat.get("jid");
|
|
|
- const view = _converse.chatboxviews.views[chat.id];
|
|
|
- const icon = view.el.querySelector(".fa-volume-up");
|
|
|
-
|
|
|
- ohun[room].p2p = json;
|
|
|
- ohun[room].sfu = false;
|
|
|
- ohun[room].view = view;
|
|
|
- connectKraken(chat, icon);
|
|
|
- });
|
|
|
- }
|
|
|
-
|
|
|
- // hack - emove me
|
|
|
- prompt.onclick();
|
|
|
- }
|
|
|
-
|
|
|
- async function handleAnswer(json)
|
|
|
- {
|
|
|
- console.debug("handleAnswer", from, json);
|
|
|
- ohun[room].ucid = json.data.track;
|
|
|
- await ohun[room].peer.setRemoteDescription(json.data.sdp);
|
|
|
- sendCandidates();
|
|
|
- }
|
|
|
-
|
|
|
- function sendCandidates()
|
|
|
- {
|
|
|
- console.debug("sendCandidates", room);
|
|
|
-
|
|
|
- if (ohun[room].candidates)
|
|
|
- {
|
|
|
- for (let i=0; i<ohun[room].candidates.length; i++)
|
|
|
- {
|
|
|
- console.debug("handleAnswer - candidate", ohun[room].candidates[i]);
|
|
|
- sendMessage('trickle', room, ohun[room].candidates[i]);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- function subscribe()
|
|
|
- {
|
|
|
- console.debug("listenForOhunEvents - subscribe", room);
|
|
|
- sendMessage('subscribe', room);
|
|
|
- }
|
|
|
-
|
|
|
- async function handleOffer(json)
|
|
|
- {
|
|
|
- console.debug("handleOffer", from, json);
|
|
|
- await ohun[room].peer.setRemoteDescription(json.data);
|
|
|
- var sdp = await ohun[room].peer.createAnswer();
|
|
|
- await ohun[room].peer.setLocalDescription(sdp);
|
|
|
- sendMessage('answer', room, sdp);
|
|
|
- }
|
|
|
-
|
|
|
- if (json_type == "response")
|
|
|
- {
|
|
|
- console.debug("Ohun Response", from, json_jid, json.id, room, json);
|
|
|
-
|
|
|
- if (json.data && json.data.sdp)
|
|
|
- {
|
|
|
- if (json.data.sdp.type === 'answer')
|
|
|
- {
|
|
|
- if (_converse.bare_jid == json_jid) handleAnswer(json);
|
|
|
- if (ohun[room].peer) setTimeout(subscribe, 1000);
|
|
|
- }
|
|
|
- else
|
|
|
-
|
|
|
- if (json.data.type === 'offer' && _converse.bare_jid == json_jid)
|
|
|
- {
|
|
|
- handleOffer(json);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- else
|
|
|
-
|
|
|
- if (json_type == "peer" && _converse.bare_jid == to)
|
|
|
- {
|
|
|
- console.debug("Ohun Request", to, from, json_jid, json.id, room, json);
|
|
|
-
|
|
|
- if (json.method === 'publish') handlePeerPublish(json);
|
|
|
- if (json.method === 'answer') handlePeerAnswer(json);
|
|
|
- if (json.method === 'trickle') handlePeerTrickle(json);
|
|
|
- if (json.method === 'end') handlePeerEnd(json);
|
|
|
- }
|
|
|
-
|
|
|
- return true;
|
|
|
-
|
|
|
- }, "urn:xmpp:json:0", 'message');
|
|
|
- }
|
|
|
-
|
|
|
- function getStunTurn()
|
|
|
- {
|
|
|
- configuration = {iceServers: [], bundlePolicy: 'max-bundle', rtcpMuxPolicy: 'require', sdpSemantics: 'unified-plan'};
|
|
|
-
|
|
|
- _converse.connection.sendIQ($iq({type: 'get', to: _converse.connection.domain}).c('services', {xmlns: 'urn:xmpp:extdisco:1'}).c('service', {host: 'turn.' + _converse.connection.domain}), function (res)
|
|
|
- {
|
|
|
- console.debug('ohun - getStunAndTurnCredentials', res);
|
|
|
-
|
|
|
- res.querySelectorAll('service').forEach(function (el)
|
|
|
- {
|
|
|
- console.debug('getStunTurn - getStunAndTurnCredentials - item', el);
|
|
|
- var dict = {};
|
|
|
-
|
|
|
- switch (el.getAttribute('type'))
|
|
|
- {
|
|
|
- case 'stun':
|
|
|
- dict.url = 'stun:' + el.getAttribute('host');
|
|
|
- if (el.getAttribute('port')) {
|
|
|
- dict.url += ':' + el.getAttribute('port');
|
|
|
- }
|
|
|
- configuration.iceServers.push(dict);
|
|
|
- break;
|
|
|
- case 'turn':
|
|
|
- dict.url = 'turn:';
|
|
|
-
|
|
|
- if (el.getAttribute('username')) {
|
|
|
- dict.username = el.getAttribute('username');
|
|
|
- }
|
|
|
- dict.url += el.getAttribute('host');
|
|
|
-
|
|
|
- if (el.getAttribute('port')) {
|
|
|
- dict.url += ':' + el.getAttribute('port');
|
|
|
- }
|
|
|
- if (el.getAttribute('transport')) {
|
|
|
- dict.url += '?transport=' + el.getAttribute('transport');
|
|
|
- }
|
|
|
- if (el.getAttribute('password')) {
|
|
|
- dict.credential = el.getAttribute('password');
|
|
|
- }
|
|
|
- configuration.iceServers.push(dict);
|
|
|
- break;
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- if (configuration.iceServers.length > 0)
|
|
|
- {
|
|
|
- configuration.iceTransportPolicy = 'relay';
|
|
|
- console.debug('getStunTurn - getStunAndTurnCredentials - config', configuration);
|
|
|
- }
|
|
|
-
|
|
|
- }, function (err) {
|
|
|
- console.warn('getting turn credentials failed', err);
|
|
|
- });
|
|
|
- }
|
|
|
-
|
|
|
- function newElement(el, id, html, className)
|
|
|
- {
|
|
|
- var ele = document.createElement(el);
|
|
|
- if (id) ele.id = id;
|
|
|
- if (html) ele.innerHTML = html;
|
|
|
- if (className) ele.classList.add(className);
|
|
|
- document.body.appendChild(ele);
|
|
|
- return ele;
|
|
|
- }
|
|
|
-
|
|
|
- function attachBadge(room, jid, flag, stream)
|
|
|
- {
|
|
|
- const chatbox = _converse.chatboxes.get(room);
|
|
|
-
|
|
|
- if (chatbox)
|
|
|
- {
|
|
|
- const occupant = chatbox.occupants.findWhere({'jid': jid});
|
|
|
- const id = occupant.get('id');
|
|
|
- const nick = occupant.get('nick');
|
|
|
- const element = document.getElementById(id);
|
|
|
-
|
|
|
- console.debug("ohun attachBadge", jid, nick, flag, room, element, stream);
|
|
|
-
|
|
|
- if (element)
|
|
|
- {
|
|
|
- const badges = element.querySelector(".occupant-badges");
|
|
|
- const html = '<a data-status="on" data-room="' + room + '" id="' + id + '" data-jid="' + jid + '" class="fas fa-volume-up" title="Voice chat" style="font-size: 20px;"></a>';
|
|
|
- let ohunEle = element.querySelector(".occupants-ohun");
|
|
|
-
|
|
|
- if (ohunEle)
|
|
|
- {
|
|
|
- ohunEle.innerHTML = html;
|
|
|
- ohunEle.style.display = "";
|
|
|
- }
|
|
|
- else {
|
|
|
- ohunEle = newElement('span', null, html, 'occupants-ohun');
|
|
|
- badges.appendChild(ohunEle);
|
|
|
-
|
|
|
- ohunEle.addEventListener('click', function(evt)
|
|
|
- {
|
|
|
- evt.stopPropagation();
|
|
|
- handleUserClick(evt.target.getAttribute("data-room"), evt.target.getAttribute("data-jid"));
|
|
|
-
|
|
|
- }, false);
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- const harker = hark(stream, {interval: 100, history: 4 });
|
|
|
-
|
|
|
- harker.on('speaking', function()
|
|
|
- {
|
|
|
- if (stream.getTracks()[0].enabled)
|
|
|
- {
|
|
|
- const icon = ohunEle.querySelector("a");
|
|
|
- //console.debug("ohun speaking", jid, ohunEle, icon);
|
|
|
- changeIcon(icon, "fa-volume-up", "green", "on");
|
|
|
- showStatusMessage(ohun[room].view, nick + ' is speaking');
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- harker.on('stopped_speaking', function()
|
|
|
- {
|
|
|
- if (stream.getTracks()[0].enabled)
|
|
|
- {
|
|
|
- const icon = ohunEle.querySelector("a");
|
|
|
- //console.debug("ohun quiet", jid, ohunEle, icon);
|
|
|
- changeIcon(icon, "fa-volume-up", "#aaa", "off");
|
|
|
- showStatusMessage(ohun[room].view, nick + ' stopped speaking');
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- ohun[room].icons[jid] = {icon: ohunEle, stream: stream, harker: harker, nick: nick};
|
|
|
-
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- function showStatusMessage(view, msg)
|
|
|
- {
|
|
|
- if (view && msg)
|
|
|
- {
|
|
|
- const div = view.el.querySelector(".message.chat-info");
|
|
|
- //console.debug("showStatusMessage", div, msg);
|
|
|
-
|
|
|
- if (div) {
|
|
|
- div.innerHTML = msg
|
|
|
- } else {
|
|
|
- view.showHelpMessages([msg]);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- function uuidv4() {
|
|
|
- return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
|
|
|
- var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
|
|
|
- return v.toString(16);
|
|
|
- });
|
|
|
- }
|
|
|
-
|
|
|
- function handleUserClick(room, jid)
|
|
|
- {
|
|
|
- const ohunEle = ohun[room].icons[jid].icon;
|
|
|
- const stream = ohun[room].icons[jid].stream;
|
|
|
- const nick = ohun[room].icons[jid].nick;
|
|
|
- const track = stream.getTracks()[0];
|
|
|
-
|
|
|
- if (ohunEle && stream)
|
|
|
- {
|
|
|
- const icon = ohunEle.querySelector("a");
|
|
|
- console.debug("ohun handleUserClick", jid, nick, ohunEle, icon);
|
|
|
-
|
|
|
- if (!track.enabled) {
|
|
|
- track.enabled = true;
|
|
|
- changeIcon(icon, "fa-volume-up", "#aaa", "on");
|
|
|
- showStatusMessage(ohun[room].view, nick + ' is unmuted');
|
|
|
- } else {
|
|
|
- track.enabled = false;
|
|
|
- changeIcon(icon, "fa-volume-up", "red", "off");
|
|
|
- showStatusMessage(ohun[room].view, nick + ' is muted');
|
|
|
- }
|
|
|
- }
|
|
|
- else log.warn("No stream or icon found");
|
|
|
- }
|
|
|
-
|
|
|
- function startVoiceChat(chat, icon)
|
|
|
- {
|
|
|
- console.debug("startVoiceChat", chat, icon);
|
|
|
-
|
|
|
- if (icon.getAttribute("data-status") == "off") {
|
|
|
-
|
|
|
- if (confirm("Ohun with " + chat.get("jid"))) connectKraken(chat, icon);
|
|
|
-
|
|
|
- } else {
|
|
|
- disconnectKraken(chat, true);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- function changeIcon(icon, newClass, color, status)
|
|
|
- {
|
|
|
- icon.classList.remove("fa-volume-off");
|
|
|
- icon.classList.remove("fa-volume-up");
|
|
|
- icon.classList.remove("fa-volume-down");
|
|
|
- icon.classList.add(newClass);
|
|
|
- icon.style.color = color;
|
|
|
- icon.style.fill = color;
|
|
|
- icon.setAttribute("data-status", status);
|
|
|
-
|
|
|
-console.log("RRRRRRRRRRRRRRRRRRRRRRRRRRRRR", icon);
|
|
|
- }
|
|
|
-
|
|
|
- function connectKraken(chat, icon)
|
|
|
- {
|
|
|
- const room = chat.get("jid");
|
|
|
- const sfu = chat.get("type") == "chatroom";
|
|
|
- console.debug("ohun connect kraken", room, sfu);
|
|
|
-
|
|
|
- setupKraken(room, sfu, icon);
|
|
|
- //setupSpeechRecognition(room);
|
|
|
- }
|
|
|
-
|
|
|
- function disconnectKraken(chat, send)
|
|
|
- {
|
|
|
- if (chat)
|
|
|
- {
|
|
|
- const room = chat.get("jid");
|
|
|
- console.debug("ohun disconnect kraken", room, ohun[room]);
|
|
|
-
|
|
|
- if (send) sendMessage('end', room);
|
|
|
-
|
|
|
- if (ohun[room])
|
|
|
- {
|
|
|
- if (ohun[room].icons && ohun[room].localStream && ohun[room].peer)
|
|
|
- {
|
|
|
- ohun[room].localStream.getTracks().forEach((track) => { track.stop() });
|
|
|
- ohun[room].peer.close();
|
|
|
-
|
|
|
- const icons = Object.getOwnPropertyNames(ohun[room].icons)
|
|
|
-
|
|
|
- for (let i=0; i<icons.length; i++)
|
|
|
- {
|
|
|
- ohun[room].icons[icons[i]].icon.style.display = "none";
|
|
|
- }
|
|
|
-
|
|
|
- updateOhunIcon(room, 'voice chat stopped', "#aaa", "off");
|
|
|
- }
|
|
|
-
|
|
|
- if (ohun[room].recognitionActive && ohun[room].recognition)
|
|
|
- {
|
|
|
- ohun[room].recognition.stop();
|
|
|
- ohun[room].recognitionActive = false;
|
|
|
- }
|
|
|
-
|
|
|
- ohun[room] = {};
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- function updateOhunIcon(room, msg, color, status)
|
|
|
- {
|
|
|
- console.debug("updateOhunIcon", room, msg, color, status, ohun);
|
|
|
-
|
|
|
- showStatusMessage(ohun[room].view, msg);
|
|
|
- changeIcon(ohun[room].icon, "fa-volume-up", color, status);
|
|
|
-
|
|
|
- const id = ohun[room].view.model.get('box_id');
|
|
|
- const item = document.getElementById('pade-active-conv-ohun-' + id);
|
|
|
-
|
|
|
- if (item)
|
|
|
- {
|
|
|
- item.style.color = color;
|
|
|
- item.style.visibility = (status == "off") ? "hidden" : "visible";
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- function getRoom()
|
|
|
- {
|
|
|
- let room = ohunRoom;
|
|
|
- if (!room) room = getSelectedChatBox().model.get("jid");
|
|
|
- return room;
|
|
|
- }
|
|
|
-
|
|
|
- function getSelectedChatBox()
|
|
|
- {
|
|
|
- var views = _converse.chatboxviews.model.models;
|
|
|
- var view = null;
|
|
|
-
|
|
|
- console.debug("getSelectedChatBox", views);
|
|
|
-
|
|
|
- for (var i=0; i<views.length; i++)
|
|
|
- {
|
|
|
- if ((views[i].get('type') === "chatroom" || views[i].get('type') === "chatbox") && !views[i].get('hidden'))
|
|
|
- {
|
|
|
- view = _converse.chatboxviews.views[views[i].id];
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
- return view;
|
|
|
- }
|
|
|
-
|
|
|
- async function setupKraken(room, sfu, icon)
|
|
|
- {
|
|
|
- console.debug("ohun setup kraken", room, sfu);
|
|
|
-
|
|
|
- ohun[room].candidates = [];
|
|
|
- ohun[room].icons= {};
|
|
|
- if (icon) ohun[room].icon = icon;
|
|
|
- ohun[room].sfu = sfu;
|
|
|
-
|
|
|
- ohun[room].peer = new RTCPeerConnection(configuration);
|
|
|
- ohun[room].peer.createDataChannel('useless');
|
|
|
-
|
|
|
- ohun[room].peer.onicecandidate = ({candidate}) =>
|
|
|
- {
|
|
|
- if (candidate)
|
|
|
- {
|
|
|
- const chat = getRoom();
|
|
|
- console.debug("candidate", ohun[room].ucid, candidate, chat);
|
|
|
-
|
|
|
- if (ohun[chat].ucid)
|
|
|
- {
|
|
|
- sendMessage('trickle', chat, candidate);
|
|
|
- }
|
|
|
- else {
|
|
|
- ohun[chat].candidates.push(candidate);
|
|
|
- }
|
|
|
- }
|
|
|
- };
|
|
|
-
|
|
|
- ohun[room].peer.ontrack = function(event)
|
|
|
- {
|
|
|
- const stream = event.streams[0];
|
|
|
- let chat = getRoom();
|
|
|
-
|
|
|
- function createAudioElement()
|
|
|
- {
|
|
|
- event.track.onmute = function(event)
|
|
|
- {
|
|
|
- console.debug("ohun onmute", event.target.id, event);
|
|
|
- }
|
|
|
-
|
|
|
- const aid = 'peer-audio-' + stream.id;
|
|
|
- let el = document.getElementById(aid);
|
|
|
-
|
|
|
- if (el) {
|
|
|
- el.srcObject = stream;
|
|
|
- } else {
|
|
|
- el = document.createElement(event.track.kind)
|
|
|
- el.id = aid;
|
|
|
- el.srcObject = stream;
|
|
|
- el.autoplay = true;
|
|
|
- el.controls = false;
|
|
|
- document.body.appendChild(el);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if (!ohun[chat].sfu || ohun[chat].p2p)
|
|
|
- {
|
|
|
- console.debug('ohun track p2p data', stream);
|
|
|
-
|
|
|
- createAudioElement();
|
|
|
- updateOhunIcon(chat, 'voice chat started', "red", "on");
|
|
|
-
|
|
|
- } else {
|
|
|
- const uname = JSON.parse(atob(decodeURIComponent(stream.id)));
|
|
|
- const json_jid = uname.jid;
|
|
|
-
|
|
|
- chat = uname.room;
|
|
|
- console.debug('ohun track sfu data', chat, json_jid, stream);
|
|
|
-
|
|
|
- if (json_jid == _converse.bare_jid)
|
|
|
- {
|
|
|
- attachBadge(chat, json_jid, true, ohun[chat].localStream);
|
|
|
- updateOhunIcon(chat, 'voice chat started', "red", "on");
|
|
|
- }
|
|
|
- else {
|
|
|
- createAudioElement();
|
|
|
- attachBadge(chat, json_jid, true, stream);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- try {
|
|
|
- const constraints = {audio: true, video: false };
|
|
|
- ohun[room].localStream = await navigator.mediaDevices.getUserMedia(constraints);
|
|
|
- console.debug("ohun - local stream", ohun[room].localStream);
|
|
|
-
|
|
|
- ohun[room].localStream.getTracks().forEach((track) => {
|
|
|
- ohun[room].peer.addTrack(track, ohun[room].localStream);
|
|
|
- });
|
|
|
-
|
|
|
- if (ohun[room].p2p) {
|
|
|
- answerStream(room);
|
|
|
- } else {
|
|
|
- publishStream(room);
|
|
|
- }
|
|
|
- } catch (err) {
|
|
|
- console.error(err);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- async function answerStream(room)
|
|
|
- {
|
|
|
- ohun[room].rnameRPC = encodeURIComponent(Strophe.getNodeFromJid(room));
|
|
|
- ohun[room].unameRPC = encodeURIComponent(btoa(JSON.stringify({room: room, jid: _converse.bare_jid, nick: _converse.nickname})));
|
|
|
-
|
|
|
- console.debug("answerStream", room, ohun[room].p2p);
|
|
|
-
|
|
|
- const data = JSON.parse(ohun[room].p2p.params[2]);
|
|
|
-
|
|
|
- await ohun[room].peer.setRemoteDescription(new RTCSessionDescription(data));
|
|
|
- var sdp = await ohun[room].peer.createAnswer();
|
|
|
- await ohun[room].peer.setLocalDescription(sdp);
|
|
|
- sendMessage('answer', room, sdp);
|
|
|
- }
|
|
|
-
|
|
|
- async function publishStream(room)
|
|
|
- {
|
|
|
- ohun[room].rnameRPC = encodeURIComponent(Strophe.getNodeFromJid(room));
|
|
|
- ohun[room].unameRPC = encodeURIComponent(btoa(JSON.stringify({room: room, jid: _converse.bare_jid, nick: _converse.nickname})));
|
|
|
-
|
|
|
- await ohun[room].peer.setLocalDescription(await ohun[room].peer.createOffer());
|
|
|
-
|
|
|
- console.debug("ohun - publish", room, ohun[room].rnameRPC, ohun[room].unameRPC, ohun[room].peer.localDescription);
|
|
|
- sendMessage('publish', room, ohun[room].peer.localDescription);
|
|
|
- }
|
|
|
-
|
|
|
- function sendMessage(method, room, payload)
|
|
|
- {
|
|
|
- console.debug("sendMessage", method, room, payload);
|
|
|
-
|
|
|
- if (ohun[room])
|
|
|
- {
|
|
|
- const target = ohun[room].sfu ? room : _converse.bare_jid;
|
|
|
- const json_type = ohun[room].sfu ? 'request' : 'peer';
|
|
|
- const type = ohun[room].sfu ? 'groupchat' : 'chat';
|
|
|
- const params = [ohun[room].rnameRPC, ohun[room].unameRPC];
|
|
|
-
|
|
|
- if (ohun[room].ucid) params.push(ohun[room].ucid);
|
|
|
- if (payload) params.push(JSON.stringify(payload));
|
|
|
-
|
|
|
- const body = JSON.stringify({id: target, method: method, params: params});
|
|
|
- _converse.connection.send($msg({type: type, to: room}).c("json",{xmlns: "urn:xmpp:json:0", type: json_type}).t(body));
|
|
|
- }
|
|
|
- else console.warn("sendMessage - voice chat not ready");
|
|
|
- }
|
|
|
-
|
|
|
- function uuidv4()
|
|
|
- {
|
|
|
- return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
|
|
|
- var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
|
|
|
- return v.toString(16);
|
|
|
- });
|
|
|
- }
|
|
|
-
|
|
|
- function setupSpeechRecognition(room)
|
|
|
- {
|
|
|
- console.debug("setupSpeechRecognition", room);
|
|
|
-
|
|
|
- ohun[room].recognition = new webkitSpeechRecognition();
|
|
|
- ohun[room].recognition.lang = _converse.locale;
|
|
|
- ohun[room].recognition.continuous = true;
|
|
|
- ohun[room].recognition.interimResults = false;
|
|
|
-
|
|
|
- ohun[room].recognition.onresult = function(event)
|
|
|
- {
|
|
|
- console.debug("Speech recog event", event)
|
|
|
-
|
|
|
- if(event.results[event.resultIndex].isFinal==true)
|
|
|
- {
|
|
|
- const transcript = event.results[event.resultIndex][0].transcript;
|
|
|
- console.debug("Speech recog transcript", transcript);
|
|
|
- ohun[room].view.model.sendMessage(transcript);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- ohun[room].recognition.onspeechend = function(event)
|
|
|
- {
|
|
|
- console.debug("Speech recog onspeechend", event);
|
|
|
- }
|
|
|
-
|
|
|
- ohun[room].recognition.onstart = function(event)
|
|
|
- {
|
|
|
- console.debug("Speech to text started", event);
|
|
|
- ohun[room].recognitionActive = true;
|
|
|
- }
|
|
|
-
|
|
|
- ohun[room].recognition.onend = function(event)
|
|
|
- {
|
|
|
- console.debug("Speech to text ended", event);
|
|
|
-
|
|
|
- if (ohun[room].recognitionActive)
|
|
|
- {
|
|
|
- console.debug("Speech to text restarted");
|
|
|
- setTimeout(function() {ohun[room].recognition.start()}, 1000);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- ohun[room].recognition.onerror = function(event)
|
|
|
- {
|
|
|
- console.debug("Speech to text error", event);
|
|
|
- }
|
|
|
-
|
|
|
- ohun[room].recognition.start();
|
|
|
- }
|
|
|
-}));
|