hero_picture
Cover Image for WebSocketでルータ越しの通信を行う

WebSocketでルータ越しの通信を行う

2014/11/18

概要

弊社ではコミュニケーションツールとして、

チャットサービスの「Slack」を使用しています。

https://slack.com/

Slackでは、

チャット内で動作するBotを簡単に作成できるような仕組みが用意されています。

とても簡単なので、色々とBotを作成していますが、

今回はその中でも「Kanasanコマンド」を作成した時に行ったテクニックをご紹介します。

Kanasan-API

社内には、Mac miniにつながっている大きなスピーカーがあります。

参考:http://seeds-std-pr.blogspot.jp/2014/09/kana-san.html

このMac miniで再生すると、

スピーカーから音が出て社内全体に響き渡るようになっています。

普段は、朝礼の時間にラジオ体操を流したり、

がんばるタイムの始めと終わりに学校のチャイムを鳴らしたりしています。

また、SayKana (http://www.a-quest.com/quickware/saykana/)というソフトを使用して、

任意の日本語文字列を喋らせたりもしてます。

単純なものですが、このシステムを「Kanasan」と名づけました。

先日、このツールと連携するKanasan-APIというWebAPIも作りました。

https://github.com/memememomo/p5-KanaSan-API

このKanasan-APIをSlackと連携させたいなと考えました。

Kanasanコマンド

Slackは、(噂では)バックエンドがIRCということもあり、

メッセージの最初にスラッシュ(/)をつけると、コマンドとして認識されます。

Slackには、新しくコマンドを作成できる仕組みも用意されています。

ということで、「/kanasan ほげほげ」とSlack上で入力したら、

社内のスピーカーに「ほげほげ」と喋らせることができるKanasanコマンドを作成しようと考えました。

メッセージ反応系botの仕組みと問題点

任意のURLを設定すると、Slack上でメッセージが投稿されたタイミングで、

設定したURLにリクエストを飛ばすことができます。

メッセージ反応系botでは、

このリクエストを処理して反応を返します。

Kanasanコマンドもこの仕組を利用します。

しかし、今回のKanasan-APIはオフィスのルータ下にあるため、

Slackに直接叩いてもらうことができません。

Websocketでルータ越しの通信

上記の問題点を解消するため、

まずはSlackに叩いてもらうエンドポイントをHeroku上に置くことにしました。

そして、Heroku上のエンドポイントとオフィス内のサーバをWebsocketでつなぎ、

なるべくリアルタイムに反応できるようにしました。

Heroku上のエンドポイントのソースは以下のようになります。

1heroku.coffee
2HTTP_PORT = process.env.PORT || 8000
3app = require("express")()
4socketio = require 'socket.io'
5server = require('http').Server(app)
6io = socketio.listen(server)
7# 社内サーバにデータを送るWebSocket
8s = undefined
9io.sockets.on 'connection', (socket) ->
10  s = socket
11  console.log 'connected'
12# Slackからのリクエストを処理
13app.get "/api", (req, res) ->
14  t = req.param 'text'
15param =
16    text:t
17if s != undefined
18    s.emit 'news', param
19    res.send 'ok'
20  else
21    res.send 'ng'
22server.listen(process.env.PORT)
23console.log HTTP_PORT
24

express と Socket.io を使用しています。

expressでSlackからのリクエストを受け付ける処理を記述し、

Socket.ioで社内サーバにWebsocket経由でデータを送る処理を記述しています。

expressとSocket.ioを連携させる機能があったので比較的簡単に書けました。

社内のサーバのソースは以下のようになります。

1local.coffee
2WEBSOCKET_URL = 'http://hogehoge.herokuapp.com/'
3API_URL = 'http://local-kanasan.net/api'
4request = require('request')
5io = require('socket.io-client')(WEBSOCKET_URL)
6# Heroku上のサーバにWebSocketで接続する
7socket = io.connect WEBSOCKET_URL
8# Herokuからのデータを受信するWebSocket
9socket.on 'news', (data) ->
10try
11# Kanasan-APIを叩く
12options =
13      uri: API_URL,
14      form: {text: data.text}
15request.post options, (error, response, body) ->
16console.log body
17catch e
18console.log 'error'
19

Socket.io と request を使用しています。

Socket.ioでHeroku上からのデータを受け付る処理を記述し、

requestでKanasanAPIを叩く処理を記述しています。

以上で、Kanasanコマンドを動作させることができました。

まとめ

WebSocketでルータ越しの通信を実現しました。

Node.jsを使用すると、今回のように双方向通信処理をカジュアルに書けていいですね。

ブラウザとの双方向通信以外の用途で色々使えそうな可能性が見えてきました。