会社のインターホン代わりを作る　＜ブラウザから電話編＞



前回SMSを飛ばすサンプルプログラムの実行までを試しましたが、今回はTwilio クライアント JavaScript APIを使用し電話をかける部分を作ります。

※ローカルの開発環境の方は、この先のTwilio クライアント JavaScript APIを使用した電話発信には外部公開サーバが必要です。ローカル環境からの発信には最下部を参照して下さい。

###1.TwiML APPSを作成する
![スクリーンショット_2015-12-24_20_37_13.png](https://qiita-image-store.s3.amazonaws.com/0/46374/c9a0150e-3a3c-f495-cbca-4422ee2c2679.png "スクリーンショット_2015-12-24_20_37_13.png")

###2.アプリ構成
・quadclient.php　（メイン画面＆コール）
・quad-client-twiml.php　（電話番号選定）
・base.css
・normalize.css https://github.com/necolas/normalize.css/

それからTwilio-phpヘルパーライブラリー
https://jp.twilio.com/docs/php/install


###3.画面を作成
こんなページを作成しました。
![スクリーンショット 2015-12-24 20.48.17.png](https://qiita-image-store.s3.amazonaws.com/0/46374/408d525a-9eb2-3ca4-59a1-ac51628755e9.png "スクリーンショット 2015-12-24 20.48.17.png")

こちらのソースコードをベースにしています。
>https://jp.twilio.com/docs/quickstart/php/client/outgoing-calls

``` quadclient.php
<?php
include '../twilio-php/Services/Twilio/Capability.php';

// put your Twilio API credentials here
$accountSid = 'ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX';
$authToken  = 'YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY';

// put your Twilio Application Sid here
// 取得したTwiml APP のSidを設定
$appSid     = 'APZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ';

// put your default Twilio Client name here
$clientName = 'QuadPhone';

// get the Twilio Client name from the page request parameters, if given
if (isset($_REQUEST['client'])) {
    $clientName = $_REQUEST['client'];
}

$capability = new Services_Twilio_Capability($accountSid, $authToken);
$capability->allowClientOutgoing($appSid);
$capability->allowClientIncoming($clientName);
$token = $capability->generateToken();
?>

<!DOCTYPE html>
<html>
  <head>
    <title>Quad Client</title>
		<meta charset="utf-8">
		<meta name="viewport" content="initial-scale=1.0">
		<link href="css/normalize.css" rel="stylesheet" type="text/css">
		<link href="css/base.css" rel="stylesheet" type="text/css">
		<link href='https://fonts.googleapis.com/css?family=Roboto:400,100,300,500,700,900' rel='stylesheet' type='text/css'>

    <script type="text/javascript"
      src="//static.twilio.com/libs/twiliojs/1.2/twilio.min.js"></script>
    <script type="text/javascript"
      src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js">
    </script>

  	</head>

    <script type="text/javascript">

      Twilio.Device.setup("<?php echo $token; ?>");

      Twilio.Device.ready(function (device) {
        $("#log").text("Client '<?php echo $clientName ?>' is ready");
      });

      Twilio.Device.error(function (error) {
        $("#log").text("Error: " + error.message);
      });

      Twilio.Device.connect(function (conn) {
        $("#log").text("Successfully established call");
      });

      Twilio.Device.disconnect(function (conn) {
        $("#log").text("Call ended");
      });

      Twilio.Device.incoming(function (conn) {
        $("#log").text("Incoming connection from " + conn.parameters.From);
        // accept the incoming connection and start two-way audio
        conn.accept();
      });

      Twilio.Device.presence(function (pres) {
        if (pres.available) {
          // create an item for the client that became available
          $("<li>", {id: pres.from, text: pres.from}).click(function () {
            $("#number").val(pres.from);
            call();
          }).prependTo("#people");
        }
        else {
          // find the item by client name and remove it
          $("#" + pres.from).remove();
        }
      });

      // クリックされた要素でコール先を変更する仕様に改変
      function call(obj) {
        // get the phone number or client to connect the call to
        params = {"PhoneNumber": obj.id};
        Twilio.Device.connect(params);
      }

      function hangup() {
        Twilio.Device.disconnectAll();
      }
    </script>
  </head>
  <body>

		<div class="title-area">
      <h1><div id="log">Loading...</div></h1>
		</div>

		<div class="container">
			<ul class="glid-block">
				<li class="glid-cell">
          <button onClick="call(this);" id="fushimi">
  					<div class="image-block">
  						<img src="img/face.png" alt="">
  					</div>
  					<p class="number">fushimi</p>
          </button>
				</li>
        
				<li class="glid-cell">
          <button onClick="call(this);" id="segawa">
						<div class="image-block">
							<img src="img/face.png" alt="">
						</div>
						<p>segawa</p>
					</button>
				</li>
				<li class="glid-cell">
          <button onClick="call(this);" id="sasaki">
						<div class="image-block">
							<img src="img/face.png" alt="">
						</div>
						<p>sasaki</p>
					</button>
				</li>
			</ul>
		</div>

		<div class="general-foot">
  		<li>
        <button class="hangup general-hangup" onclick="hangup();">
          Hangup
        </button>
  		</li>
  		<li>
        <button class="call general-call" onclick="call(this);" id="office">
          Call to main phone
        </button>
  		</li>
		</div>
  </body>
</html>

```

``` quad-client-twiml.php
<?php
header('Content-type: text/xml');

// put a phone number you've verified with Twilio to use as a caller ID number
$callerId = "+16698004184";

// put your default Twilio Client name here, for when a phone number isn't given
$number   = "+81358296297";

$member = array("fushimi" => "+818000000000",
                "segawa" =>  "+818000000001",
                "sasaki" =>  "+818000000002",
                "office" => "+813000000003");

// 受け取ったパラメータの名前でコール先を決定
if (isset($_REQUEST['PhoneNumber'])) {
  if (array_key_exists(htmlspecialchars($_REQUEST['PhoneNumber']),$member)){
    $number = $member[htmlspecialchars($_REQUEST['PhoneNumber'])];
  }
}

// wrap the phone number or client name in the appropriate TwiML verb
// by checking if the number given has only digits and format symbols
if (preg_match("/^[\d\+\-\(\) ]+$/", $number)) {
    $numberOrClient = "<Number>" . $number . "</Number>";
} else {
    $numberOrClient = "<Client>" . $number . "</Client>";
}
?>

<Response>
    <Dial callerId="<?php echo $callerId ?>">
          <?php echo $numberOrClient ?>
    </Dial>
</Response>

```


``` base.css
button{
  border: none;
  background: none;
}

/* line 4, ../scss/_original.scss */
body {
  background-color: #f7f7f7;
  font-family: 'Roboto', sans-serif;
  position: relative; }

/* line 10, ../scss/_original.scss */
.title-area {
  padding: 40px 0; }
/* line 13, ../scss/_original.scss */
.title-area h1 {
  font-size: 48px;
  font-weight: 100;
  text-align: center;
  margin: 0; }

/* line 21, ../scss/_original.scss */
.container {
  display: block;
  overflow: scroll;
  padding: 0 10%;
  width: 100%;
  max-height: 76%; }

/* line 38, ../scss/_original.scss */
.glid-block {
  display: block;
  width: 100%;
  height: auto;
  padding: 0;
  margin: 0; }
/* line 45, ../scss/_original.scss */
.glid-block:after {
  content: "";
  display: table;
  clear: both; }

/* line 52, ../scss/_original.scss */
.glid-cell {
  display: block;
  width: 33.333333%;
  padding: 16px 20px;
  float: left; }
/* line 59, ../scss/_original.scss */
.glid-cell a {
  display: block;
  width: 100%;
  height: 100%;
  color: #333;
  text-decoration: none; }
/* line 68, ../scss/_original.scss */
.glid-cell .image-block {
  display: block;
  width: 100%;
  border-radius: 50%;
  background-color: #fff;
  overflow: hidden;
  line-height: 0; }
/* line 76, ../scss/_original.scss */
.glid-cell .image-block img {
  width: 100%; }
/* line 81, ../scss/_original.scss */
.glid-cell p {
  text-align: center;
  font-size: 16px; }

/* line 88, ../scss/_original.scss */
.general-foot {
  display: table;
  width: 100%;
  padding: 40px 10%;
  position: fixed;
  bottom: 0;
  background-color: #f7f7f7; }
/* line 96, ../scss/_original.scss */
.general-foot li{
  display: table-cell;
  vertical-align: middle;
  text-align: center;
  width: 50%;
  padding-left: 10px;
  padding-right: 10px;
}
.general-foot .general-call {
  display: block;
  text-align: center;
  text-decoration: none;
  font-size: 16px;
  color: #fff;
  width: 100%;
  border-radius: 24px;
  padding: 1em;
  background-color: #4dce52; }
.general-foot .general-hangup {
  display: block;
  text-align: center;
  text-decoration: none;
  font-size: 16px;
  color: #fff;
  width: 100%;
  border-radius: 24px;
  padding: 1em;
  background-color: #e65172; }

/* line 10, ../scss/base.scss */
* {
  -webkit-box-sizing: border-box;
  -moz-box-sizing: border-box;
  -ms-box-sizing: border-box;
  -o-box-sizing: border-box;
  box-sizing: border-box; }

/* line 19, ../scss/base.scss */
html {
  font-size: 62.5%; }
@media screen and (max-width: 800px) {
  /* line 19, ../scss/base.scss */
  html {
    font-size: 54.7%; } }

/* line 29, ../scss/base.scss */
body {
  font-size: 1.0rem;
  line-height: 1;
  color: #363636;
  word-wrap: break-word;
  vertical-align: middle; }

/* line 42, ../scss/base.scss */
html, body {
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 0;
  position: relative; }

```


今回iPadでいい感じに見えるように組んで、いざiPad Air(iOS9.1)で検証してみるとTwilio APIがLoadingのまま止まってしまい、iPadのSafariでは動かなかった。
PCの方はOSX(El Capitan)のSafari9で動作確認済み。
iPadで使えるようにするにはアプリ化するか、と思ったクリスマスイブの夜でした。。


おまけ。
ローカル開発環境から電話発信する場合にはApp SIDが不要な下記の記述で可能です。

``` php
<?php
// Get the PHP helper library from twilio.com/docs/php/install
require_once('twilio-php/Services/Twilio.php'); // 環境に合わせて変えて下さい
 
// Your Account Sid and Auth Token from twilio.com/user/account
$sid = "ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"; 
$token = "YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY"; 
$client = new Services_Twilio($sid, $token);
 
$call = $client->account->calls->create("+1NNNNNNNNNN", "+81NNNNNNNNNN", "http://demo.twilio.com/docs/voice.xml", array());
// echo $call->sid;
```

最終的には社内に居てSlackで着席状況を確認してコール可能な人だけ、表示するなどステータス更新できると良いなと。
Slack botも強化が必要そうだ。。