https://d226lax1qjow5r.cloudfront.net/blog/blogposts/phone-call-web-browser-nexmo-in-app-voice-vue-js-dr/click-to-call-phils-post.png

Vue.jsとVonageを使ってウェブブラウザから電話をかける

最終更新日 November 6, 2020

所要時間:9 分

このブログポストでは、ウェブブラウザから電話をかける方法について説明します。 Vonageアプリ内音声を使って電話をかける方法を説明します。In-App Voiceと アプリ内メッセージングは開発者プレビュー版ですので、開発体験や提供される機能についてのフィードバックをお待ちしています。ご連絡は Vonage開発者コミュニティSlack.

ウェブ・ブラウザから電話をかけるには、アプリにいくつかのコンポーネントが必要になる。ブラウザで実行され、JavaScript用のVonage Client SDKを使用するVue.JSアプリケーション、User JWTを生成してアプリケーションユーザーをVonageで認証するために使用するアプリケーションサーバー、そして電話を受けるための電話機です。

下のシーケンス図は、アプリを構築した後の動作を示しています。このブログ記事では、まず電話番号を入力できるUIを持つVue.JSアプリケーションを作成します。次に、必要なユーザーJWTを生成できるアプリケーション・サーバーを作成します。アプリサーバーが稼働したら、Vue.JSアプリを更新してJWTを取得し、Vonage Client SDK for JavaScriptでそれを使ってVonageプラットフォームにログインし、電話を開始します。その後、Vonageが電話をどのように進めるかの指示を取得するために行うGETリクエストを処理するために、アプリサーバーを更新する必要があります。これらの指示は、WebブラウザのVue.JSアプリケーションから電話に電話を接続するようにVonageに伝えます。

/* https://bramp.github.io/js-sequence-diagrams/ 参加者 Vue.JS App as V Participant App Server as A Participant Nexmo as N Participant Phone as P V->A: Get User JWT Note of V: In a production app<br/>this request should<br/>be authenticaterd A-->V:User JWT V -> V: Create Nexmo<br/>Conversation Client V -> N: Login N --> V: Logged In V -> N: Call Phone N -> A: GET answer_url A --> N: NCCO connect N -> P: Call */.

Call from Web Browser Sequence DiagramCall from Web Browser Sequence Diagram

そのため、いくつかのステップを踏む必要があるが、その価値は十分にある。

もしコードに直接飛び込みたいのであれば、以下を参照してほしい。 ブラウザからの呼び出しコードをご覧ください。

始める前に

  • ヤーンパッケージ管理用

  • Vue CLI Vue CLIを使うことで、アプリを雛形化し、開発サーバーを動かすことができます。

  • A NexmoアカウントSDKの使用と電話の発信を可能にするため

  • Nexmo CLI Nexmo CLIを使えば、コマンドラインからNexmoアプリケーションを素早く作成・設定できます。 CLIのベータ版をご利用ください。 npm install -g nexmo@beta

  • 地元のトンネルソリューション NgrokNexmoプラットフォームがローカルで動作しているウェブサーバーに到達できるようにします)。このブログ記事ではNgrokを使用します。

それでは始めよう。

新しいVue.JSプロジェクトをScaffoldする

ターミナルで vueコマンドを実行し、デフォルトの (babel, eslint)を選ぶ。

$ vue create call-from-browser

# navigate into the newly created Vue project folder
cd call-from-browser

その結果、次のようなディレクトリ構造とファイルができる:

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.lock

これから CallFromBrowserコンポーネントを構築することにしよう。 HelloWorldの名前を変更しましょう。

mv src/components/HelloWorld.vue src/components/CallFromBrowser.vue

これでUIの構築を始める準備ができた。

シンプルな電話番号入力とダイヤルインターフェースの作成

Vue.JSには強力で成長中のエコシステムがあるので、既存のコンポーネントがあればそれを利用するのは理にかなっている。幸いなことに、いくつかの選択肢があり、ここでは vue-tel-inputコンポーネントによる スティーブン・ダオ.

vue-tel-input example animationvue-tel-input example animation

コンポーネントをインストールする:

yarn add vue-tel-input

電話番号の検証を助けるコンポーネントができたので、それを CallFromBrowserコンポーネントに追加します。開く src/components/CallFromBrowser.vueをコードエディタで開きます。

要素を次のように更新する。 <template>要素を次のように更新する:

<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>

テンプレートは <vue-tel-input>コンポーネントを利用し @onInputハンドラを設定します。私たちは <button>を持ちます。 call-in-progressクラスが callInProgressメソッドを呼び出すクリックハンドラがあります。 controlCallClickメソッドを呼び出します。テンプレートには <p>{{infoMessage}}</p>プロパティを通してユーザーからのフィードバックを提供することができます。 data.infoMessageプロパティがあります。

次に、同じファイル内の <script>タグの内容を更新してみよう。

<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>

この data関数は phoneオブジェクトを返します。 onInputオブジェクトを返します。ハンドラでは、ユーザが入力した電話番号を vue-tel-inputコンポーネントに入力された電話番号と、その電話番号の有効性と国を表すプロパティを設定します。

また、電話番号の有効性に関するフィードバックをユーザーに提供するために infoMessageプロパティを設定します。Vueのデータバインディングは、ここで設定した値がUIに反映されることを意味します。

スタブ controlCallClickメソッドも追加されている。 <button>も追加されている。

コンポーネントを設定する最後のステップは CallFromBrowserコンポーネントを設定する最後のステップは、スタイルを追加することです。既存の <style>要素と内容を次のように置き換える:

<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>

スタイルは contentのデフォルトを <button>を赤い電話絵文字 (☎️) に設定します。もし call-in-progressクラスが存在する場合 callInProgressプロパティが trueが返された場合 contentは代わりに古いスタイルの電話の受話器の絵文字(?)になります。

基本的なUIを整えるための最後のステップは、次のようにアップデートすることだ。 App.vuetemplateそして scriptタグを置き換えることだ。と styleタグはそのままにしておきます。

<template>
  <div id="app">
    <callfrombrowser>
  </callfrombrowser></div>
</template>

<script>
import CallFromBrowser from './components/CallFromBrowser.vue'

export default {
  name: 'app',
  components: {
    CallFromBrowser
  }
}
</script>

置換 <template>内容を置き換える。 CallFromBrowser.vueコンポーネント定義をインポートし、インポートされたコンポーネントを登録する。

これでアプリを実行できる:

yarn serve

この状態で、ブラウザで http://localhost:8080コンポーネントに電話番号を入力してみる。 vue-input-telコンポーネントに電話番号を入力してみます。アプリのUI下部に電話番号が表示されます。

Call from Browser simple user interfaceCall from Browser simple user interface

VonageプラットフォームにログインするためのユーザーJWTの作成

Nexmo Stitch JavaScript SDKはVonageプラットフォームに接続し、ウェブブラウザ内でIn-App Voice機能を実現します。Nexmoプラットフォームに接続するためには、以下のものが必要です。 loginアプリケーション・ユーザー用の有効なユーザー認証JWT(JSON Web Token)が必要です。ユーザーJWTを作成するには、いくつかのものを作成する必要があります:

  1. で取得できるユーザーJWTを生成するシンプルなサーバーです。 CallFromBrowserVue.JSコンポーネント

  2. Vonageプラットフォーム内のアプリケーション - Nexmo CLIを使用してこれを行うことができます。

  3. 現在のウェブアプリケーションユーザーのアプリケーション内ユーザー

まずは簡単なサーバーを作ってみよう。まず serverディレクトリを作成し、いくつかの依存関係をインストールして index.js.envファイルを作成します。

mkdir server cd server yarn init -yp #create package.json for server yarn add express body-parser cors dotenv nexmo touch index.js touch .env # for environment variables

サーバーには Express.jsを使用する。 dotenvファイルを読み込むために .envファイルを読み込むために使用する。また Nexmo Node.JSライブラリライブラリもインストールしました。

サーバコードを見る前に、アプリケーションとそのアプリケーションのユーザを作成しましょう。これはNexmo CLIを使って行います:

nexmo app:create call-from-browser https://example.com/answer https://example.com/event --keyfile=private.key

このコマンドを実行すると、アプリケーションIDが出力されます。また、アプリケーションの詳細を .nexmo-appファイルに追加します。アプリケーションIDを取り出し .envファイルに追加します。 private.keyの場所です:

NEXMO_PRIVATE_KEY=private.key
NEXMO_APP_ID=YOUR_APPLICATION_ID

アプリケーションのセットアップの最後は、アプリケーション内でユーザーを作成することです。Nexmoのライブラリを使用してこれを行うことも可能ですが、今回はNexmo CLIを使用してユーザーを設定します:

nexmo user:create name=demo

このコマンドは、ファイル内で特定されたアプリケーションIDのユーザーを作成します。 .nexmo-appファイル内で特定されたアプリケーションIDのユーザーを作成する。ユーザー名の環境変数を .envファイルに追加します。

NEXMO_PRIVATE_KEY=private.key
NEXMO_APP_ID=YOUR_APPLICATION_ID
NEXMO_APP_USER_NAME=demo

次に index.jsを開き、基本的なサーバー・コードを追加する:

// 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!'))

上記のコードでは、Expressを読み込み、インバウンドリクエストをJSONとして解析するように設定している(これは後で使用する)。また、Cross-Origin Resource Sharing(CORS)をサポートするようにExpressを設定する。これは、Vue.JSアプリが localhost:8080で実行されており、ブラウザで実行されているJavaScriptコードは、別のポートである localhost:3000に呼び出す必要があるためです。

これで node index.jsを実行し http://localhost:3000/no-authを実行し、エンドポイントが期待通りの JSON を返すことを確認します。

User JWT with null valueUser JWT with null value

では、Vonage Client SDK for JavaScriptで使用するUser JWTを生成するコードを追加しましょう。

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})
})

変数 userAcl変数は、アプリケーションID、ユーザー名の subはユーザー名、そして expはJWTの有効期限です。詳細は JWTとACLの概要を参照してください。

ノードの再起動 index.jsにアクセスすると http://localhost:3000/no-authにアクセスすると、本物のJWTが生成されたことがわかります。

User JWT with real JWT valueUser JWT with real JWT value

注:時には JWTデバッガーを見て、JWTの内容をチェックするのが便利な場合があります。

ウェブ・ブラウザーからユーザーJWTをフェッチする

JWT生成の準備が整ったので、サーバーで作成したユーザーJWTを取得するためにクライアントに戻ることができる。

サーバーのURLを100%ハードコードするのはバッドプラクティスなので、Vue.JSのプロパティで設定できるようにします。

...

      callInProgress: false
    }
  },

  props: {
    jwtUrl: {
      type: String,
      default: process.env.VUE_APP_JWT_URL || "http://localhost:3000/no-auth"
    }
  },

この jwtUrlプロパティを設定することで上書きできます。 jwt-urlプロパティを <CallFromBrowser>要素に defaultコンポーネントの構築時に VUE_APP_JWT_URL値を .envファイルに値を設定することで、コンポーネントがビルドされたときに変更できます。詳細は Vue.JS propsおよび Vue CLI 3 環境変数とモード.

サーバーURLが設定されたので、次は fetchユーザーJWTを設定します。Vue.JSにはさまざまな ライフサイクルフック.フック内でJWTを取得します。 createdフックの中でJWTを取得します。そのために created関数を CallFromBrowser関数を定義します。

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)
      })
  },

Vue.JS開発サーバーがまだ実行中であることを確認します。 yarn serveを実行してください。 call-from-browserディレクトリから実行) http://localhost:8080にアクセスし、開発者ツールを開いてコンソールを確認し、ユーザJWTがログに記録されていることを確認してください。

JWT now present in browser and output via console.logJWT now present in browser and output via console.log

JavaScript用Vonage Client SDKの追加

クライアントにUser JWTがあれば、JavaScript用のVonage Client SDKを含めることができます:

cd .. # navigate up from the 'server' directory yarn add nexmo-stitch

次に、SDKを CallFromBrowser.vueコンポーネントに組み込み ConversationClientオブジェクト定義をインポートします:

<script>
import 'vue-tel-input/dist/vue-tel-input.css'
import VueTelInput from 'vue-tel-input'

import ConversationClient from 'nexmo-stitch'

必要な定義が含まれたら、新しいインスタンスを作成する必要があります。 ConversationClientそして login.ユーザーJWTを取得した後に、これを実行します:

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)
    })
  },

ログインPromiseが解決した後、アプリケーションの表現への参照を app変数を通してアプリケーションの表現への参照を受け取ります。そのアプリの参照を将来の使用のために保持し (this.app)、そしてアプリの2つのイベントにバインドします。

にバインドする。 member:callにバインドする。イベントハンドラでは、現在の呼び出しへの参照を this.call.

また call:status:changedをバインドして 呼び出しステータス.イベントハンドラ内で callInProgressプロパティを更新する。呼が最終ステータスのいずれかにある場合、呼は進行中ではない。それ以外の場合、呼のステータスは進行中である。これらの状態は、テンプレートの <button>に反映される。

ウェブブラウザーから電話をかける

サーバーに最後のアップデートを行い、アプリを完成させる前に、クライアントで最後にしなければならないことは、ユーザーが <button>.

    ...
    },

    controlCallClick() {
      if(this.callInProgress) {
        this.call.hangUp()
      }
      else if(this.phone.isValid) {
        this.app.callPhone(this.phone.number)
      }
    }

上記で controlCallClickを更新し、進行中の通話がなく、ユーザーが有効な電話番号を入力したかをチェックするロジックを追加しました。通話中であれば、通話を切断します。どちらの場合も、適切な関数を this.callイベントハンドラで設定された member:callイベントハンドラで設定された参照に対して、適切な関数を呼び出します。

クライアント側の機能がすべて整っているので、有効な電話番号を入力し、通話ボタンをクリックすると、Vonageプラットフォームからのエラーがコンソールに表示されます。

会話:エラー:見つかりません

conversation-not-found message in browser consoleconversation-not-found message in browser console

Nexmoプラットフォームによって通話が開始または受信されると、Nexmoプラットフォームは関連するNexmoアプリケーションに対してHTTPリクエストを行います。 answer_urlにHTTPリクエストを行う。HTTPリクエストを受信したサーバーはNexmo Conversation Control Object (NCCO)を返さなければなりません。

ブラウザと電話の接続

戻る server/index.jsエンドポイントを追加する /answerエンドポイントを追加します。 GETエンドポイントを追加する:

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)
})

Nexmoは、呼び出しの進め方を指示するJSON構造体、NCCOが返されることを期待している。上記の nccoJSON構造体は、Nexmoに以下を通知する。 connectへの呼び出しを通知する。 phoneエンドポイントへの呼び出しを通知します。 req.query.to- の値で識別される番号を持つエンドポイントへの呼び出しをNexmoに通知します。 toで識別される番号でエンドポイントを呼び出すことを通知します。この番号は this.app.callPhoneに渡した番号です。

_注釈

  1. ウェブアプリにはアプリケーションレベルの認証がないので、自分で追加する必要があることを覚えておいてください。 req.query.toreq.query.fromで識別される)ユーザーがリクエストされた呼び出しを許可されていることを確認します。 fromで識別される) ユーザーがリクエストされた呼び出しを許可されていることを確認します。

  2. Nexmoのバーチャル電話番号をお持ちの場合は、その電話番号に NEXMO_FROM_NUMBERエントリを .envというエントリーを追加する必要があります。そうしないと、"プライベート番号 "または "不明 "と表示される可能性があります。

サーバーのNodeプロセスを再起動し、更新されたコードで実行されるようにする。

最後に、NexmoプラットフォームがアンサーURLに到達できるようにする必要があります。これを行うには、Ngrokを使ってローカルトンネルを作成する。 localhost:3000.

$ ngrok http 3000

Ngrok output in a terminalNgrok output in a terminalそして、Nexmoアプリケーションの answer_urlNexmo CLI を使用して Ngrok トンネル URL を利用するように Nexmo アプリケーションを更新します。

$ nexmo app:update NEXMO_APP_ID "call-from-browser" https://4ca73ac6.ngrok.io/answer https://4ca73ac6.ngrok.io/event

NEXMO_APP_IDserver/.envまたは server/.nexmo-app

ブラウザでVue.JSアプリに戻り、電話番号を入力してボタンをクリックすると、ウェブブラウザから発信できます。

Full Vue.JS Call from Browser application working alongside screenshot of phone ringingFull Vue.JS Call from Browser application working alongside screenshot of phone ringing

結論

このブログ記事の目的は、Vue.JSとVonage Client SDK for JavaScriptを使ったIn-App Voiceを使って、ウェブブラウザから地球上のどの電話にも直接電話をかけることができるアプリの作り方を紹介することです。基本的なことを説明し、これがどのようなユースケースを可能にするのかのヒントになれば幸いです。また、アプリをアップデートして次のようにすることもできます。 着信電話をサポートする.

この記事の冒頭で述べたように、In-App Voiceは開発者プレビュー版です。 VonageコミュニティSlack.

次のページ

このブログ記事が面白かったなら、以下のリソースもチェックする価値がある:

シェア:

https://a.storyblok.com/f/270183/400x400/73e68604be/phil-leggetter.jpg
Phil Leggetter

Phil is Head of Developer Relations at Hookdeck, an asynchronous messaging platform, and a proud Vonage alumni.