
Compartilhar:
Phil is Head of Developer Relations at Hookdeck, an asynchronous messaging platform, and a proud Vonage alumni.
Como fazer chamadas telefônicas a partir de um navegador da Web com Vue.js e Vonage
Tempo de leitura: 20 minutos
Observação: algumas das ferramentas ou métodos descritos neste artigo podem não ter mais suporte ou estar desatualizados. Para obter conteúdo atualizado ou suporte, consulte nossas postagens mais recentes ou nossa Documentação
Nesta postagem do blog, vamos explicar como você pode fazer uma ligação de um navegador da web para um telefone usando o o recurso de In-App Voice no aplicativo da Vonage , utilizando o Vonage Client SDK para JavaScript e Vue.JS. O In-App Voice e o In-App Messaging estão em fase de Developer Preview; por isso, adoraríamos receber seus comentários tanto sobre a experiência de desenvolvimento quanto sobre as funcionalidades oferecidas. Você pode entrar em contato pela Slack da Comunidade de Desenvolvedores da Vonage.
Para fazer uma ligação telefônica a partir de um navegador da web, precisaremos de vários componentes em nosso aplicativo. Um aplicativo Vue.JS que seja executado no navegador e utilize o Vonage Client SDK para JavaScript, um servidor de aplicativos usado para autenticar o usuário do aplicativo na Vonage por meio da geração de um JWT de usuário e um telefone para receber a ligação.
O diagrama de sequência abaixo mostra como tudo funcionará depois que tivermos criado nosso aplicativo. Nesta postagem do blog, vamos primeiro criar o aplicativo Vue.JS com uma interface de usuário que permita a inserção de um número de telefone. Em seguida, criaremos um servidor de aplicativos capaz de gerar o JWT de usuário necessário. Assim que o servidor de aplicativos estiver em funcionamento, atualizaremos o aplicativo Vue.JS para recuperar o JWT e usá-lo com o Vonage Client SDK para JavaScript a fim de fazer login na plataforma Vonage e iniciar a chamada telefônica. Em seguida, precisaremos atualizar o servidor de aplicativos para lidar com uma solicitação GET que a Vonage fará a fim de recuperar instruções sobre como prosseguir com a chamada telefônica. Essas instruções indicarão à Vonage para conectar a chamada do aplicativo Vue.JS no navegador da web a um telefone.
/* https://bramp.github.io/js-sequence-diagrams/ Aplicativo Vue.JS do participante como V; Servidor do aplicativo do participante como A; Participante Nexmo como N; Telefone do participante como P; V->A: Obter JWT do usuário. Observação à direita de V: Em um aplicativo de produção <br/>, essa solicitação deve <br/> ser autenticada A-->V: JWT do usuário V -> V: Criar cliente de conversação Nexmo <br/> V -> N: Login N --> V: Conectado V -> N: Ligar para o telefone N -> A: Obter answer_url A --> N: Conectar NCCO N -> P: Ligar */
Call from Web Browser Sequence Diagram
Então, são alguns passos, mas o resultado vale a pena.
Se você preferir ir direto para o código, pode encontrar o código “Call from Browser” no GitHub.
Antes de começarmos
Fios para gerenciamento de pacotes
O Vue CLI para criar a estrutura inicial do nosso aplicativo e executar um servidor de desenvolvimento
A Account da Nexmo para que possamos usar o SDK e fazer chamadas telefônicas
O CLI do Nexmo permite criar e configurar rapidamente um aplicativo Nexmo a partir da linha de comando. Use a versão beta da CLI, por exemplo:
npm install -g nexmo@betaUma solução de túnel local, como Ngrok , para que a plataforma Nexmo possa acessar um servidor web em execução local). Para esta postagem do blog, vamos usar o Ngrok.
Com tudo isso pronto, vamos começar.
Criar um novo projeto Vue.JS com estrutura de base
Execute o seguinte vue no terminal e selecione a opção padrão (babel, eslint) quando solicitado.
$ vue create call-from-browser
# navigate into the newly created Vue project folder
cd call-from-browserIsso fará com que você tenha a seguinte estrutura de diretórios e arquivos:
call-from-browser
├── README.md
├── babel.config.js
├── package.json
├── public
│ ├── favicon.ico
│ └── index.html
├── src
│ ├── App.vue
│ ├── assets
│ │ └── logo.png
│ ├── components
│ │ └── HelloWorld.vue
│ └── main.js
└── yarn.lockVamos criar um CallFromBrowser componente, então vamos renomear o HelloWorld que foi criado.
Com isso, estamos prontos para começar a criar a interface do usuário.
Criar uma interface simples para inserção e discagem de números de telefone
O Vue.JS possui um ecossistema sólido e em crescimento; portanto, faz sentido utilizar os componentes já existentes, caso haja algum. Felizmente, existem algumas opções, e vamos optar pelo componente vue-tel-input de Steven Dao.
vue-tel-input example animation
Instale o componente:
Agora que temos o componente para nos ajudar a validar números de telefone, podemos adicioná-lo ao CallFromBrowser componente. Abra-o src/components/CallFromBrowser.vue em um editor de código.
Atualize o <template> elemento da seguinte maneira:
<template>
<main class="call-from-browser">
<vue-tel-input @oninput="onInput">
</vue-tel-input>
<button class="call-control" v-bind:class="{'call-in-progress': callInProgress}" v-on:click="controlCallClick"></button>
<p>{{infoMessage}}</p>
</main>
</template>O modelo utiliza o <vue-tel-input> componente e define um @onInput manipulador. Temos um <button> que terá um call-in-progress classe definida com base em um callInProgress valor de propriedade e um manipulador de clique que chamará um controlCallClick método. O modelo também possui um <p>{{infoMessage}}</p> que nos permite fornecer algum feedback ao usuário por meio de uma data.infoMessage propriedade que estamos prestes a definir.
A seguir, vamos atualizar o conteúdo da <script> tag dentro do mesmo arquivo.
<script>
import 'vue-tel-input/dist/vue-tel-input.css'
import VueTelInput from 'vue-tel-input'
export default {
name: 'CallFromBrowser',
components: {
'vue-tel-input': VueTelInput
},
...
</code></pre>
<p>This imports the CSS and the component definition for the telephone input component sets the name of the component to <code>CallFromBrowser</code> and registers the <code>vue-tel-input</code> component dependency so it can be used within the template.</p>
<p>Next let's set up some properties for data binding such as the <code>callInProgress</code> property relied upon by the <code>template</code> and add any methods that are expected to be in place, as show, in the template:</p>
<pre><code class="language-javascript">export default {
name: 'CallFromBrowser',
components: {
'vue-tel-input': VueTelInput
},
data() {
return {
phone: {
number: '',
isValid: false,
country: {}
},
infoMessage: "",
callInProgress: false
}
},
methods: {
onInput({ number, isValid, country }) {
this.phone.number = number;
this.phone.isValid = isValid;
this.phone.country = country;
if(!isValid) {
this.infoMessage = "Please enter a valid phone number"
}
else {
this.infoMessage = `Thanks for entering a valid ${this.phone.country.name} phone number`
}
},
controlCallClick() {
}
}
}
</script>A data função retorna um phone objeto que preenchemos no onInput manipulador. Nesse manipulador, definimos o número de telefone que o usuário inseriu no vue-tel-input componente, além de propriedades que representam a validade do número de telefone e o país ao qual ele pertence.
Também fornecemos ao usuário um feedback sobre a validade do número de telefone ao definir a infoMessage propriedade. A vinculação de dados do Vue significa que o valor que definimos aqui é refletido na interface do usuário.
Um método controlCallClick também é adicionado para lidar com o <button> clique.
Uma etapa final na configuração do CallFromBrowser componente é adicionar alguns estilos. Substitua o <style> elemento e seu conteúdo pelos seguintes:
<style scoped="">
.vue-tel-input {
width: 200px;
margin: auto;
}
.call-control {
font-size: 11em;
}
.call-control:before {
content: '☎️';
}
.call-control.call-in-progress:before {
content: '?'
}
</style>O estilo define o padrão content do <button> como o emoji do telefone vermelho (☎️). Se a call-in-progress classe estiver presente, defina dinamicamente se a callInProgress propriedade retornar true , então o content será, em vez disso, um emoji de aparelho telefônico antigo (?).
A etapa final para implementar a interface de usuário básica é atualizar App.vue substituindo o template e script . Deixe a style tag como está.
<template>
<div id="app">
<callfrombrowser>
</callfrombrowser></div>
</template>
<script>
import CallFromBrowser from './components/CallFromBrowser.vue'
export default {
name: 'app',
components: {
CallFromBrowser
}
}
</script>Substituir <template> o conteúdo, importar a CallFromBrowser.vue definição do componente e registrar o componente importado.
Agora podemos executar o aplicativo:
Com isso em execução, acesse no navegador http://localhost:8080 e tentar inserir números de telefone no vue-input-tel componente. Veremos o número de telefone ser validado na parte inferior da interface do aplicativo.
Call from Browser simple user interface
Criação de um JWT de usuário para fazer login na plataforma da Vonage
O SDK do Nexmo Stitch para JavaScript se conecta à plataforma da Vonage para habilitar a funcionalidade de In-App Voice dentro do navegador da web. Para nos conectarmos à plataforma da Nexmo, precisamos login um JWT (JSON Web Token) de autenticação válido para o usuário do aplicativo, que define as permissões desse usuário. Para criar um JWT de usuário, precisaremos criar alguns itens:
um servidor simples que gera o JWT do usuário, o qual pode ser recuperado pelo
CallFromBrowsercomponente Vue.JSan Application within the Vonage platform — podemos fazer isso usando a CLI da Nexmo
um usuário no aplicativo para os usuários atuais do aplicativo web
Vamos começar criando um servidor simples. Crie um server diretório, instale algumas dependências e crie um index.js e .env arquivos para as funcionalidades de que precisamos.
Para o servidor, vamos usar o Express.js com os middlewares CORS e body-parser. O `dotenv` é usado para carregar o .env arquivo, que, por sua vez, conterá a configuração que não queremos manter no controle de versão. Também instalamos a biblioteca Nexmo para Node.JS para auxiliar na geração do JWT do usuário.
Antes de analisarmos o código do servidor, vamos também criar o aplicativo e o usuário para esse aplicativo. Podemos fazer isso usando a CLI do Nexmo:
Ao executar este comando, será exibido um ID do aplicativo. Além disso, os detalhes do aplicativo serão adicionados a um .nexmo-app arquivo. Pegue o ID do aplicativo e adicione-o ao .env arquivo, juntamente com uma variável para o private.key localização:
NEXMO_PRIVATE_KEY=private.key
NEXMO_APP_ID=YOUR_APPLICATION_IDA última etapa da configuração do aplicativo é criar um usuário dentro do aplicativo. É possível fazer isso usando as bibliotecas da Nexmo, mas, neste caso, vamos configurar um usuário usando a CLI da Nexmo:
Este comando criará o usuário para o ID do aplicativo identificado no .nexmo-app arquivo. Adicione uma variável de ambiente com o nome do usuário ao .env arquivo.
NEXMO_PRIVATE_KEY=private.key
NEXMO_APP_ID=YOUR_APPLICATION_ID
NEXMO_APP_USER_NAME=demoAgora abra index.js para adicionar o código básico do servidor:
// Load .env config
require('dotenv').config({
path: __dirname + '/.env'
});
const Nexmo = require('nexmo')
const express = require('express')
const bodyParser = require('body-parser')
const cors = require('cors')
const app = express()
app.use(bodyParser.json())
app.use(cors())
// endpoint that doesn't authenticate the user
// it will simply return a JWT with every request
app.get('/no-auth', (req, res) => {
res.json({userJwt: null})
})
app.listen(3000, () => console.log('Example app listening on port 3000!'))No código acima, carregamos o Express e o configuramos para analisar as solicitações recebidas como JSON (usaremos isso mais tarde). Também configuramos o Express para oferecer suporte ao Cross-Origin Resource Sharing (CORS). Isso é necessário porque o aplicativo Vue.JS está sendo executado em localhost:8080 e o código JavaScript em execução no navegador precisa fazer uma chamada para esse servidor, que está rodando em localhost:3000, uma porta diferente.
Agora você pode executar node index.js e, em seguida, acessar http://localhost:3000/no-auth para garantir que o endpoint retorne o JSON esperado.
User JWT with null value
Agora, vamos adicionar o código para gerar o JWT do usuário a ser usado com o Vonage Client SDK para JavaScript.
const userAcl = {
"paths": {
"/v1/users/**": {},
"/v1/conversations/**": {},
"/v1/sessions/**": {},
"/v1/knocking/**": {}
}
}
// endpoint that doesn't authenticate the user
// it will simply return a JWT with every request
app.get('/no-auth', (req, res) => {
const jwt = Nexmo.generateJwt(process.env.NEXMO_PRIVATE_KEY, {
application_id: process.env.NEXMO_APP_ID,
sub: process.env.NEXMO_APP_USER_NAME,
exp: new Date().getTime() + 86400,
acl: userAcl
})
res.json({userJwt: jwt})
})As userAcl variáveis fornecem um conjunto de reivindicações ou regras de acesso que são utilizadas na criação do JWT, juntamente com o ID do aplicativo, um sub para o nome do usuário e um exp como prazo de validade do JWT. Consulte a visão geral do JWT e da ACL no Nexmo Developer para obter mais informações.
Reiniciando o nó index.js o processamento e o acesso http://localhost:3000/no-auth mostrará que um JWT válido foi gerado.
User JWT with real JWT value
Observação: às vezes, pode ser útil dar uma olhada no o JWT Debugger para verificar o conteúdo do seu JWT.
Obtendo o JWT do usuário a partir do navegador da Web
Com a geração do JWT concluída, podemos voltar ao cliente para recuperar o JWT do usuário que criamos no servidor.
Como não é recomendável codificar a URL do servidor de forma rígida a 100%, vamos permitir que ela seja configurada por meio de uma propriedade do Vue.JS, com um valor padrão que seja prático para nossa configuração de desenvolvimento.
...
callInProgress: false
}
},
props: {
jwtUrl: {
type: String,
default: process.env.VUE_APP_JWT_URL || "http://localhost:3000/no-auth"
}
},O jwtUrl valor pode então ser sobrescrito definindo-se uma jwt-url propriedade no <CallFromBrowser> elemento, e o default pode ser alterado quando o componente é compilado, definindo um VUE_APP_JWT_URL valor definido em um .env arquivo em nosso diretório raiz. Para mais informações, consulte props do Vue.JS e Variáveis de ambiente e modos do Vue CLI 3.
Com a URL do servidor definida, agora podemos fetch o JWT do usuário. O Vue.JS possui vários ganchos de ciclo de vida. Vamos recuperar o JWT dentro do created hook. Para isso, defina uma created função dentro da CallFromBrowser definição.
props: {
jwtUrl: {
type: String,
default: process.env.VUE_APP_JWT_URL || "http://localhost:3000/no-auth"
}
},
created() {
fetch(this.$props.jwtUrl)
.then(response => {
return response.json();
})
.then(json => {
console.log(json)
})
.catch(error => {
console.error(error)
})
},Verifique se o servidor de desenvolvimento do Vue.JS ainda está em execução (execute yarn serve de dentro do call-from-browser diretório, caso contrário), acesse http://localhost:8080 e abra suas ferramentas de desenvolvedor para verificar o console e garantir que o JWT do usuário esteja registrado.
JWT now present in browser and output via console.log
Incorporando o Vonage Client SDK para JavaScript
Com o JWT do usuário no cliente, podemos prosseguir com a inclusão do Vonage Client SDK para JavaScript:
Em seguida, inclua o SDK no CallFromBrowser.vue componente e importe a ConversationClient definição do objeto:
<script>
import 'vue-tel-input/dist/vue-tel-input.css'
import VueTelInput from 'vue-tel-input'
import ConversationClient from 'nexmo-stitch'Depois de incluir a definição necessária, precisamos criar uma nova instância do ConversationClient e login. Faremos isso após recuperar o JWT do usuário:
created() {
fetch(this.$props.jwtUrl)
.then(response => {
return response.json();
})
.then(json => {
this.conversationClient = new ConversationClient({debug: true})
return this.conversationClient.login(json.userJwt)
})
.then(app => {
this.app = app
// When the active member (the user) makes a call
// keep a reference to the Call object so we can
// hang up later
this.app.on("member:call", (member, call) => {
this.call = call
});
// Keep track of call status so we know how to
// interact with the call e.g. hangup
this.app.on("call:status:changed", (call) => {
this.callInProgress =
[
"machine",
"timeout",
"unanswered",
"rejected",
"busy",
"failed",
"completed"
].indexOf(call.status) === -1;
})
})
.catch(error => {
console.error(error)
})
},Depois que a Promise de login é resolvida, recebemos uma referência a uma representação do aplicativo por meio da app variável. Mantemos uma referência a esse aplicativo para uso futuro (this.app) e também associamos dois eventos ao aplicativo.
Nós associamos a member:call que é acionado quando o usuário ativo faz uma chamada. No manipulador de eventos, armazenamos uma referência à chamada atual com this.call.
Também nos comprometemos a call:status:changed para acompanhar o status da chamada. Dentro do manipulador de eventos, atualizamos a callInProgress propriedade com base no status da chamada. Se a chamada estiver em qualquer um dos estados finais, ela não está em andamento. Caso contrário, o status da chamada é “em andamento”. Esses estados serão refletidos no <button> no modelo.
Como fazer uma ligação pelo navegador da web
A última coisa que precisamos fazer no cliente — antes de enviarmos uma última atualização para o servidor e concluirmos o aplicativo — é tratar o clique do usuário no <button>.
...
},
controlCallClick() {
if(this.callInProgress) {
this.call.hangUp()
}
else if(this.phone.isValid) {
this.app.callPhone(this.phone.number)
}
}Acima, atualizamos controlCallClick a lógica para verificar se não há nenhuma chamada em andamento e se o usuário digitou um número de telefone válido; isso deve iniciar a chamada. Se houver uma chamada ativa, isso deve encerrar a chamada. Em ambos os casos, chamamos a função apropriada na this.call referência que foi definida no member:call manipulador de eventos.
Com toda a funcionalidade do lado do cliente já implementada, você pode digitar um número de telefone válido, clicar no botão de chamada e, em seguida, verá um erro no console proveniente da plataforma da Vonage
conversação:erro:não encontrado
conversation-not-found message in browser console
Quando uma chamada é iniciada ou recebida pela plataforma Nexmo, ela envia uma solicitação HTTP para um answer_url para a aplicação Nexmo associada relevante. O servidor que recebe essa solicitação HTTP deve retornar um Objeto de Controle de Conversação da Nexmo (NCCO); um conjunto de instruções que informa à Nexmo como proceder com a chamada.
Conectando o navegador a um celular
Voltando a server/index.js adicionar um /answer ponto de extremidade para lidar com a GET solicitação da plataforma Vonage:
app.get('/answer', (req, res) => {
const ncco = [{
"action": "connect",
"from": process.env.NEXMO_FROM_NUMBER,
"endpoint": [{
"type": "phone",
"number": req.query.to
}]
}]
res.json(ncco)
})A Nexmo espera que seja retornada uma estrutura JSON, o NCCO, informando como proceder com a chamada. A ncco que retornamos informa à Nexmo para connect a chamada para um phone ponto de extremidade com o número identificado pelo valor em req.query.to - o to parâmetro de consulta na solicitação GET de entrada. Esse número é o número que passamos para this.app.callPhone em nosso aplicativo Vue.JS.
_Notas:
Lembre-se de que não temos autenticação no nível do aplicativo em nosso aplicativo web; portanto, você precisará adicioná-la por conta própria. Por exemplo, no endpoint da URL de resposta, você pode verificar o
req.query.toereq.query.frompara garantir que o usuário (identificado porfrom) tenha permissão para realizar a chamada solicitada.Se você tiver um número de telefone virtual da Nexmo, deve adicionar uma
NEXMO_FROM_NUMBERentrada no.envarquivo para que os destinatários das chamadas vejam um número na tela ao receberem a ligação. Caso contrário, a chamada poderá aparecer como “Número Privado” ou “Desconhecido”._
Reinicie o processo do Node no servidor para que ele seja executado com o código atualizado.
Por fim, precisamos permitir que a plataforma Nexmo acesse a URL de resposta. Para isso, use o Ngrok para criar um túnel local para localhost:3000.
$ ngrok http 3000
Ngrok output in a terminalE atualize o answer_url para que sua aplicação Nexmo utilize as URLs do túnel Ngrok por meio da CLI do Nexmo.
$ nexmo app:update NEXMO_APP_ID "call-from-browser" https://4ca73ac6.ngrok.io/answer https://4ca73ac6.ngrok.io/event
Observação: você pode encontrar o
NEXMO_APP_IDemserver/.envouserver/.nexmo-app
Volte ao aplicativo Vue.JS no navegador, digite um número de telefone e clique no botão para fazer uma chamada de saída a partir do seu navegador.
Full Vue.JS Call from Browser application working alongside screenshot of phone ringing
Conclusão
O objetivo desta postagem no blog era mostrar como criar um aplicativo que permita ao usuário ligar para qualquer telefone do mundo diretamente de um navegador da web, usando o Vue.JS e o recurso de voz no aplicativo (In-App Voice) por meio do Vonage Client SDK para JavaScript. Ela apresenta os conceitos básicos e, espero, tenha servido de inspiração para os casos de uso que isso pode possibilitar. Ah, e você também pode atualizar o aplicativo para suportar chamadas recebidas.
Conforme mencionado no início desta postagem, o In-App Voice está em fase de Developer Preview; portanto, entre em contato conosco se tiver alguma opinião sobre a experiência que teve ao desenvolver este aplicativo ou se tiver qualquer outro comentário, participando da Slack da Comunidade Vonage.
Para onde vamos agora?
Se você achou este post interessante, vale a pena conferir também os seguintes recursos: