|
|
"use strict"; |
|
|
// |
|
|
// Na Documentação incluir os sites da simplesip.com.br no Content-Security-Policy |
|
|
// |
|
|
const UASimplesIP = (function () { |
|
|
function carregandoScriptSimpleSIP(url, callback) { |
|
|
let script = document.createElement("script"); |
|
|
script.src = url; |
|
|
script.async = true; // Asynchronous loading |
|
|
script.onload = function () { |
|
|
if (typeof callback === "function") { |
|
|
callback(); |
|
|
} |
|
|
// Clean up after script execution |
|
|
script.onload = script.onerror = null; |
|
|
document.head.removeChild(script); |
|
|
}; |
|
|
script.onerror = function () { |
|
|
// Handle errors |
|
|
console.error("Error loading script:", url); |
|
|
if (typeof callback === "function") { |
|
|
callback(new Error("Error loading script")); |
|
|
} |
|
|
// Clean up after script execution |
|
|
script.onload = script.onerror = null; |
|
|
document.head.removeChild(script); |
|
|
}; |
|
|
document.head.appendChild(script); |
|
|
} |
|
|
|
|
|
carregandoScriptSimpleSIP("./appWebrtc/simplesiplib.js", (error) => { |
|
|
if (!error) { |
|
|
console.log("::: Script loaded successfully!"); |
|
|
} else { |
|
|
console.error(":::: Failed to load script:", error); |
|
|
} |
|
|
}); |
|
|
|
|
|
class EventEmitter { |
|
|
constructor() { |
|
|
this.events = {}; |
|
|
} |
|
|
|
|
|
on(eventName, listener) { |
|
|
if (!this.events[eventName]) { |
|
|
this.events[eventName] = []; |
|
|
} |
|
|
this.events[eventName].push(listener); |
|
|
} |
|
|
|
|
|
emit(eventName, ...args) { |
|
|
if (this.events[eventName]) { |
|
|
this.events[eventName].forEach((listener) => listener(...args)); |
|
|
} |
|
|
} |
|
|
|
|
|
off(eventName, listener) { |
|
|
if (this.events[eventName]) { |
|
|
this.events[eventName] = this.events[eventName].filter( |
|
|
(fn) => fn !== listener |
|
|
); |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
const eventoSip = new EventEmitter(); |
|
|
|
|
|
function Autenticacao(PROTOCOLO, SERVIDOR, PORTA, NOME, RAMAL, SENHA) { |
|
|
this.PROTOCOLO = PROTOCOLO; |
|
|
this.SERVIDOR = SERVIDOR; |
|
|
this.PORTA = PORTA; |
|
|
this.NOME = NOME; |
|
|
this.RAMAL = RAMAL; |
|
|
this.SENHA = SENHA; |
|
|
} |
|
|
|
|
|
const _Autenticacao = new Autenticacao(); |
|
|
|
|
|
const incomingCallAudio = new window.Audio( |
|
|
"https://cdn.pixabay.com/download/audio/2021/08/04/audio_bb630cc098.mp3?filename=short-success-sound-glockenspiel-treasure-video-game-6346.mp3" |
|
|
); |
|
|
|
|
|
const outgoingCallAudio = new window.Audio( |
|
|
"https://cdn.pixabay.com/download/audio/2022/03/14/audio_461a4fb0c4.mp3?filename=phonesound-65797.mp3" |
|
|
); |
|
|
|
|
|
const endCallAudio = new window.Audio( |
|
|
"https://cdn.pixabay.com/download/audio/2022/03/17/audio_5eaa35a92e.mp3?filename=mobile_phone_hanging_up-94525.mp3" |
|
|
); |
|
|
|
|
|
incomingCallAudio.loop = true; |
|
|
outgoingCallAudio.loop = true; |
|
|
const localAudio = new window.Audio(); |
|
|
localAudio.autoplay = true; |
|
|
const remoteAudio = new window.Audio(); |
|
|
remoteAudio.autoplay = true; |
|
|
|
|
|
let phone; |
|
|
let session; |
|
|
let peerConnection = null; |
|
|
|
|
|
function iniciandoAutenticacaonoPBX() { |
|
|
const socket = new SimpleSIP.WebSocketInterface( |
|
|
_Autenticacao.PROTOCOLO + |
|
|
"://" + |
|
|
_Autenticacao.SERVIDOR + |
|
|
":" + |
|
|
_Autenticacao.PORTA + |
|
|
"/ws" |
|
|
); |
|
|
const configuration = { |
|
|
uri: "sip:" + _Autenticacao.RAMAL + "@" + _Autenticacao.SERVIDOR, |
|
|
password: _Autenticacao.SENHA, |
|
|
sockets: [socket], |
|
|
session_timers: false, |
|
|
no_answer_timeout: 180, |
|
|
hack_via_tcp: false, |
|
|
hack_via_ws: true, |
|
|
display_name: |
|
|
_Autenticacao.NOME !== null ? _Autenticacao.NOME : _Autenticacao.RAMAL, |
|
|
user_agent: "UASimpleSIP 1.2.0", |
|
|
contact_uri: |
|
|
"sip:" + |
|
|
_Autenticacao.RAMAL + |
|
|
"@" + |
|
|
_Autenticacao.SERVIDOR + |
|
|
";transport=" + |
|
|
_Autenticacao.PROTOCOLO, |
|
|
pcConfig: { |
|
|
iceServers: [ |
|
|
{ |
|
|
urls: "stun:stun.l.google.com:19302", |
|
|
}, |
|
|
{ |
|
|
urls: "stun:stun1.l.google.com:19302", |
|
|
}, |
|
|
{ |
|
|
urls: "stun:stun2.l.google.com:19302", |
|
|
}, |
|
|
], |
|
|
}, |
|
|
}; |
|
|
if (configuration.uri && configuration.password) { |
|
|
phone = new SimpleSIP.UA(configuration); |
|
|
|
|
|
phone.on("registrationFailed", function (ev) { |
|
|
console.log("Registering on SIP server failed with error: " + ev.cause); |
|
|
configuration.uri = null; |
|
|
configuration.password = null; |
|
|
}); |
|
|
|
|
|
phone.on("newRTCSession", function (ev) { |
|
|
const newSession = ev.session; |
|
|
|
|
|
if (session) { |
|
|
session = null; |
|
|
} |
|
|
//Para quando estou criando uma conexao, ou seja, estou realizando uma ligacao |
|
|
if (newSession.connection) { |
|
|
peerConnection = newSession.connection; |
|
|
console.log("criou peerConnection", peerConnection); |
|
|
} |
|
|
//Para quando estou recebendo uma conexao |
|
|
newSession.on("peerconnection", (event) => { |
|
|
peerConnection = event.peerconnection; |
|
|
console.log("recebeu o peerConnection", peerConnection); |
|
|
}); |
|
|
|
|
|
//RECEBENDO UMA CHAMADA |
|
|
if (newSession.direction === "incoming") { |
|
|
incomingCallAudio.play(); |
|
|
eventoSip.emit("incomingcall", newSession.remote_identity.uri.user); |
|
|
|
|
|
newSession.on("ended", () => { |
|
|
session = null; |
|
|
remoteAudio.srcObject = null; |
|
|
incomingCallAudio.pause(); |
|
|
eventoSip.emit("home"); |
|
|
if (peerConnection) { |
|
|
peerConnection.close(); |
|
|
peerConnection = null; |
|
|
} |
|
|
}); |
|
|
|
|
|
newSession.on("failed", () => { |
|
|
session = null; |
|
|
remoteAudio.srcObject = null; |
|
|
incomingCallAudio.pause(); |
|
|
eventoSip.emit("home"); |
|
|
if (peerConnection) { |
|
|
peerConnection.close(); |
|
|
peerConnection = null; |
|
|
} |
|
|
}); |
|
|
} else { |
|
|
//REALIZANDO UMA CHAMADA |
|
|
eventoSip.emit("outgoingcall"); |
|
|
newSession.on("ended", () => { |
|
|
session = null; |
|
|
remoteAudio.srcObject = null; |
|
|
incomingCallAudio.pause(); |
|
|
eventoSip.emit("home"); |
|
|
if (peerConnection) { |
|
|
peerConnection.close(); |
|
|
peerConnection = null; |
|
|
} |
|
|
}); |
|
|
|
|
|
newSession.on("failed", () => { |
|
|
session = null; |
|
|
remoteAudio.srcObject = null; |
|
|
incomingCallAudio.pause(); |
|
|
eventoSip.emit("home"); |
|
|
if (peerConnection) { |
|
|
peerConnection.close(); |
|
|
peerConnection = null; |
|
|
} |
|
|
}); |
|
|
} |
|
|
|
|
|
//QUANDO FINALIZA UMA CHAMADA |
|
|
newSession.on("ended", () => { |
|
|
remoteAudio.srcObject = null; |
|
|
session = null; |
|
|
endCallAudio.play(); |
|
|
eventoSip.emit("confirmedEnded"); |
|
|
}); |
|
|
|
|
|
//QUANDO UMA CHAMADA EH REJEITADA |
|
|
newSession.on("failed", () => { |
|
|
incomingCallAudio.pause(); |
|
|
outgoingCallAudio.pause(); |
|
|
remoteAudio.srcObject = null; |
|
|
session = null; |
|
|
eventoSip.emit("home"); |
|
|
}); |
|
|
|
|
|
newSession.on("confirmed", function (event) { |
|
|
// Verifica se session.connection esta definida |
|
|
if (peerConnection) { |
|
|
//Obter as tracks locais |
|
|
const senders = peerConnection.getSenders(); |
|
|
const localStream = new MediaStream(); |
|
|
senders.forEach((sender) => { |
|
|
if (sender.track) { |
|
|
localStream.addTrack(sender.track); |
|
|
} |
|
|
}); |
|
|
//Atribuir ao localAudio |
|
|
localAudio.srcObject = localStream; |
|
|
//Volume definido para nao se ouvir, mas caso futuramente precise de midia de video, ja esta pronto. |
|
|
localAudio.volume = 0; |
|
|
|
|
|
//Obter as tracks remotas |
|
|
const receivers = peerConnection.getReceivers(); |
|
|
const remoteStream = new MediaStream(); |
|
|
|
|
|
receivers.forEach((receiver) => { |
|
|
if (receiver.track) { |
|
|
remoteStream.addTrack(receiver.track); |
|
|
} |
|
|
}); |
|
|
remoteAudio.srcObject = remoteStream; |
|
|
remoteAudio.volume = 1; |
|
|
} |
|
|
|
|
|
//PAUSA TODOS OS AUDIOS DE FEEDBACK |
|
|
incomingCallAudio.pause(); |
|
|
outgoingCallAudio.pause(); |
|
|
|
|
|
eventoSip.emit("incall", newSession.remote_identity.uri.user); |
|
|
}); |
|
|
|
|
|
newSession.on("newDTMF", function (event) { |
|
|
console.log("DTMF recebido:", event.dtmf.tone); |
|
|
}); |
|
|
|
|
|
newSession.on("icecandidate", function (event) { |
|
|
if ( |
|
|
event.candidate.type === "srflx" && |
|
|
event.candidate.relatedAddress !== null && |
|
|
event.candidate.relatedPort !== null |
|
|
) { |
|
|
event.ready(); |
|
|
} |
|
|
}); |
|
|
|
|
|
session = newSession; |
|
|
}); |
|
|
|
|
|
phone.on("registered", function (e) { |
|
|
eventoSip.emit("statusChange", "registered"); |
|
|
}); |
|
|
|
|
|
phone.on("unregistered", function (e) { |
|
|
eventoSip.emit("statusChange", "unregistered"); |
|
|
}); |
|
|
|
|
|
phone.on("registrationFailed", function (e) { |
|
|
eventoSip.emit("statusChange", "registrationFailed"); |
|
|
}); |
|
|
|
|
|
phone.on("connected", function (e) { |
|
|
eventoSip.emit("statusChange", "connected"); |
|
|
}); |
|
|
|
|
|
phone.on("disconnected", function (e) { |
|
|
eventoSip.emit("statusChange", "disconnected"); |
|
|
}); |
|
|
phone.start(); |
|
|
} |
|
|
} |
|
|
|
|
|
//Finalizar chamada |
|
|
eventoSip.on("endCall", () => { |
|
|
if (session) { |
|
|
eventoSip.emit("confirmedEnded"); |
|
|
session.terminate(); |
|
|
session = null; |
|
|
} |
|
|
}); |
|
|
|
|
|
//Transferir diretamente a chamada |
|
|
eventoSip.on("transferir", (data) => { |
|
|
if (session) { |
|
|
session.refer(data); |
|
|
} |
|
|
}); |
|
|
|
|
|
//Mutar chamada |
|
|
eventoSip.on("mute", () => { |
|
|
if (session) { |
|
|
session.mute(); |
|
|
console.log(session?.isMuted()); |
|
|
} |
|
|
}); |
|
|
|
|
|
//Desmutar chamada |
|
|
eventoSip.on("unmute", () => { |
|
|
if (session) { |
|
|
session.unmute(); |
|
|
console.log(session?.isMuted()); |
|
|
} |
|
|
}); |
|
|
|
|
|
//Enviar DTMF |
|
|
eventoSip.on("enviarDTMF", (data) => { |
|
|
if (session) { |
|
|
session.sendDTMF(data); |
|
|
} |
|
|
}); |
|
|
|
|
|
//Aceitar chamada |
|
|
eventoSip.on("accepted", (data) => { |
|
|
try { |
|
|
if (session) { |
|
|
session.answer({ |
|
|
mediaConstraints: { |
|
|
audio: true, |
|
|
video: false, |
|
|
}, |
|
|
}); |
|
|
eventoSip.emit("incall"); |
|
|
} |
|
|
} catch (error) { |
|
|
console.error("Erro ao aceitar chamada: ", error); |
|
|
} |
|
|
}); |
|
|
|
|
|
eventoSip.on('loginAutomatico', event => { |
|
|
if(UASimplesIP.config){ |
|
|
console.log(UASimplesIP.config) |
|
|
iniciandoAutenticacaonoPBX(); |
|
|
} |
|
|
}) |
|
|
|
|
|
function ativaDebug(value) { |
|
|
value === true |
|
|
? SimpleSIP.debug.enable("SimpleSIP:*") |
|
|
: SimpleSIP.debug.disable("SimpleSIP:*"); |
|
|
} |
|
|
|
|
|
return { |
|
|
config: _Autenticacao, |
|
|
|
|
|
start: function () { |
|
|
if ( |
|
|
_Autenticacao.PROTOCOLO === undefined || |
|
|
_Autenticacao.SERVIDOR === undefined || |
|
|
_Autenticacao.RAMAL === undefined || |
|
|
_Autenticacao.SENHA === undefined |
|
|
) { |
|
|
console.error( |
|
|
":::: Configura<EFBFBD><EFBFBD>o do Usu<EFBFBD>rio Inv<EFBFBD>lida para Autentica<EFBFBD><EFBFBD>o no PBX !" |
|
|
); |
|
|
} else { |
|
|
ativaDebug(false); |
|
|
iniciandoAutenticacaonoPBX(); |
|
|
} |
|
|
}, |
|
|
|
|
|
realizaUmaChamada: (numero) => { |
|
|
const mediaConstraintsCall = { |
|
|
audio: true, |
|
|
video: false, |
|
|
}; |
|
|
phone |
|
|
? phone.call(numero, mediaConstraintsCall) |
|
|
: eventoSip.emit("error"); |
|
|
}, |
|
|
|
|
|
ativaDebug: (value) => { |
|
|
ativaDebug(value); |
|
|
}, |
|
|
|
|
|
unregister: () => { |
|
|
phone |
|
|
? phone.unregister() |
|
|
: console.warn("Sem ramal cadastrado para apagar"); |
|
|
}, |
|
|
|
|
|
estouRegistradonoPBX: () => { |
|
|
console.log("estouRegistradonoPBX ::: " + phone.isRegistered()); |
|
|
}, |
|
|
|
|
|
simplesipEvento: eventoSip, |
|
|
|
|
|
help: function () { |
|
|
console.log("========================================================"); |
|
|
console.log("=== Unified Communications - Simples Ip - Version 1.2 ==="); |
|
|
console.log("========================================================"); |
|
|
}, |
|
|
}; |
|
|
})();
|
|
|
|