Dígitro Neo Interact CTI Computer Telephony Integration |
Como foi mencionado anteriormente, a conexão SSE é uma conexão HTTP persistente que permite ao servidor enviar dados de forma assíncrona ao cliente que estabeleceu a conexão. A implementação em javascript desta conexão é feita pela interface EventSource, amplamente documentada na internet, podendo ser utilizada tanto pelo navegador (browser) como em back-end em node.js. O exemplo abaixo foi escrito em node.js (javascript), porém pode ser portado para o navegador utilizando-se a especificação do EJS (import) no lugar da especificação CJS (require).
A abertura de um canal SSE no Neo Interact deve ser feita através do endpoint:
A execução bem sucedida deste comando estabelece um canal SSE persistente por onde é retornado o evento on_open_channel com o identificador deste canal (channel_id), como mostra o exemplo abaixo. Este channel_id é necessário para operações futuras, incluindo a assinatura de eventos, login de agente e fechamento do canal.
Exemplo: O código exemplo abaixo estabelece um canal SSE ao Neo Interact, obtém o channel_id, aguarda 2 minutos e encerra a conexão
const EventSource = require('eventsource')
const fetch = require('node-fetch')
function openSseChannel(ctiUrl, apiKey) {
return (new Promise((resolve, reject) => {
const url = `${ctiUrl}/sse/open_channel`
sseChannel = new EventSource(url, { headers: { 'X-API-KEY': apiKey } })
sseChannel.ctiUrl = ctiUrl
sseChannel.apiKey = apiKey
sseChannel.on('open', (msg) => {
console.log(`open`, msg)
})
sseChannel.on('error', (err) => {
console.log(`error`, err)
})
sseChannel.on('message', (msg) => {
console.log(`message`, msg)
})
sseChannel.on('on_open_channel', (msg) => {
console.log('on_open_channel', msg)
const data = JSON.parse(msg.data)
sseChannel.id = Number(data.channel_id)
resolve(sseChannel)
})
}))
}
async function closeSseChannel(sseChannel) {
sseChannel.close()
const headers = {
'X-API-KEY': sseChannel.apiKey,
'Content-Type': 'application/json',
}
const body = JSON.stringify({
channel_id: sseChannel.id,
})
await fetch(`${sseChannel.ctiUrl}/sse/close_channel`, { method: 'POST', headers, body })
console.log(`closeSseChannel - channelId:${sseChannel.id}`)
}
const ctiUrl = 'https://<domínio-cliente>.saas.digitro.cloud/neo/cti/v1.0.0/' // Substitua pela a URL de seu domínio
const apiKey = '1f9cdebf-387c-4dc5-a0cd-ac873132ba90' // Substitua pela sua API-KEY
openSseChannel(ctiUrl, apiKey).then(sseChannel => {
console.log(`SSE channel id: ${sseChannel.id}`)
setTimeout(async () => {
await closeSseChannel(sseChannel)
}, 120000)
})
Ao executar este exemplo, os eventos recebidos são mostrados no terminal. Logo após o início da execução do código será apresentado a saída:
open Event { type: 'open' }
on_open_channel MessageEvent {
type: 'on_open_channel',
data: '{"channel_id":1}',
lastEventId: '',
origin: <domínio-cliente>.saas.digitro.cloud'
}
SSE channel id: 1
Indicando a abertura do canal SSE e o recebimento do evento "on_open_channel" com o identificador do canal SSE aberto.
O header "X-API-KEY" é utilizado para transportar a API-KEY, necessária para autenticação do acesso à API CTI do Neo Interact. A escolha do envio dessa chave num cabeçalho HTTP busca seguir padrões utilizado para este fim, e embora a chave esteja em texto aberto no cabeçalho, ela não fica completamente exposta como na URL. Embora até o momento a classe built-in EventSource em navegadores não tenha suporte à headers, algumas soluções alternativas disponívels na internet podem ser usados para este fim.
Apesar de ainda recomendar o transporte da API-KEY num cabeçalho HTTP, a API CTI do Neo Interact prevê uma forma alternativa para o estabelecimento de conexões SSE, passando-se a API-KEY na query string da URL para conexão, como descrito no documento.
Uma variação do código exemplo acima, com a passagem da API-KEY via query string seria:
(...)
const querystring = require('querystring')
function openSseChannel(ctiUrl, apiKey) {
return (new Promise((resolve, reject) => {
const url = `${ctiUrl}/sse/open_channel?${querystring.encode({ apiKey })}`
sseChannel = new EventSource(url)
(...)
É importante ressaltar que essa exceção só vale para este comando de abertura de canal SSE, por conta de limitações na implementação da classe 'EventSource' nos navegadores. Todos os demais comandos devem transportar a API-KEY no cabeçalho HTTP 'X-API-KEY'.
Para que uma conexão HTTP se mantenha de forma persistente é necessário que haja tráfego de pacotes do servidor ao cliente com certa frequência, caso contrário a conexão será derrubada por time-out. O Neo Interact a cada determinado período de tempo sem tráfego na conexão, envia eventos denominados "heartbeating" com formato "data: {}":
message MessageEvent {
type: 'message',
data: '{}',
lastEventId: '',
origin: 'https://<domínio-cliente>.saas.digitro.cloud'
}
message MessageEvent {
type: 'message',
data: '{}',
lastEventId: '',
origin: 'https://<domínio-cliente>.saas.digitro.cloud'
}
A interface EventSource reconecta automaticamente ao servidor em caso de queda da conexão, então caso o cliente deseje encerrar a conexão, este deve executar a função close(), além de sinalizar ao Neo Interact que está fechando intencionalmente o canal pelo endpoint:
Ao final do timer do código exemplo o canal é fechado, tanto pela interface EventSource, como sinalizado ao Neo Interact
closeSseChannel - channelId:1
Client
(Backend/Browser) NAT/Firewall NeoInteract
| | |
| | |
| GET /sse/open_channel |
|-------------------------------------------->|
| open_channel event (channel_id) |
|<--------------------------------------------|
| | |
| | |
| | events |
|<--------------------------------------------|
| heartbeating (data = {}) |
|<--------------------------------------------|
| heartbeating (data = {}) |
|<--------------------------------------------|
| | events |
|<--------------------------------------------|
| | |
| | |
| | |
| POST /sse/close_channel |
|-------------------------------------------->|
| | 200 OK |
|<--------------------------------------------|