node.jsを使って簡単にwebサイトにインスタントメッセンジャーを組み込む方法(その1)
node.jsを使ってWebサイトにインスタントメッセンジャー
(Google Talkでも、Yahoo!メッセンジャーでも、MSN Messengerでも、Facebook ChatでもXMPP対応のもの)
を組み込む方法の紹介(というかメモ)です。
デモ
(IEだとAjaxでエラーになっているっぽいです。ライブラリはIEでも動くっぽいので何かがおかしいのだと思いますが、何がおかしいか分かる人いたら教えてください。)
XMPPって何よ?
あんまり理解してなくてもWebに組み込む程度なら出来ますが、詳しく知りたい人は自分で調べてください。
Extensible Messaging and Presence Protocol - Wikipedia
Webサイトで対応する場合の仕組み
XMPPはHTTPプロトコルでは無いので、直接ブラウザから使用する事はできません。
なんらかの手段でHTTPとXMPPの変換する必要がありますが、この変換にはBOSHというプロトコルが使われている様です。
XMPP Technologies: BOSH
つまり、
Webサイト ─(HTTP)→ BOSHサーバ ─(XMPP)→ IMサーバ
という流れになります。
構成
今回はBOSHサーバーの実装として、node.js上で動くnode-xmpp-boshを使う事にします。
BOSHクライアントはStrophe.jsというJavaScriptライブラリを使います。
ブラウザ(Strophe.js) ─(HTTP)→ node-xmpp-bosh ─(XMPP)→ IMサーバ
の流れです。
今回はHerokuでNode.jsを動かしてみます。
Node.js 0.4.7
Strophe.js
node-xmpp-bosh
ここでは、開発マシンは、VMPlayer上のUbuntu11.04を使用します。(CentOS5.6でも大体同じ感じでいけました。)
node.jsとnpmのインストール
node.jsのインストールは簡単なので、公式ガイド等をみながらnpmのインストールまでやってください。
バージョンは最新だとherokuやnode-xmpp-boshに対応しないので、0.4.7にしてください。
https://github.com/joyent/node/wiki/Installation
なんかわからない事があったりうまくいかない事があったら自分で調べてください。
これからnode.jsを始める人のためのインストール系エントリまとめ
herokuの設定
公式のガイドがとてもわかりやすいので、公式ガイドをみながらサンプルのHello Worldを作ってみてください(無料範囲で作れます)。
今回はデータベースは使わないのでSQL Databaseのところは飛ばしても大丈夫です。
http://devcenter.heroku.com/articles/node-js
node-xmpp-boshインストール
これも簡単で、
「npm install node-xmpp-bosh」でいけます。
参考
とりあえずHerokuで動かすだけならこれで十分ですが、
↓ここ等を参考に、いろいろ設定してみても良いと思います。
https://code.google.com/p/node-xmpp-bosh/wiki/DebianHowTo
boshパッケージと、boshに必要なパッケージの設定
herokuの設定中に作成した、package.jsonのdependenciesに必要パッケージを追加します。
node-xmpp-boshをインストールしたディレクトリにあるpackage.jsonを参考に追加すれば良いと思います。
自分の場合は↓のように追加しました。
{ "name": "node-example", "version": "0.0.1", "dependencies": { "dns-srv":"0.0.6", "eventpipe":"0.0.3", "jsdom":"0.2.0", "htmlparser":"1.7.3", "request":"1.9.5", "ltx":"0.0.5", "node-expat":"1.3.2", "node-uuid":"1.2.0", "tav":"0.1.0", "underscore":"1.1.6", "node-xmpp-bosh":"0.2.3" } }
Heroku上でnode-xmpp-boshを動かす
boshをインストールしたディレクトリにある、
- bosh.conf.example.js
- run-server.js
をプロジェクトフォルダにコピーしてください。
bosh.conf.example.jsのファイル名をbosh.conf.jsに変更して、少々修正します。
exports.config = { port: process.env.PORT || 5280, //←Herokuに割り当てられたportを動的に設定する設定 host: '0.0.0.0', //これはこのままでOK path: /^\/http-bind(\/+)?$/, //これ以降は必要に応じて変更してください。 logging: 'INFO', // The maximum number of bytes that the BOSH server will // "hold" from the client max_data_held: 100000,
次に、run-server.jsを修正します。
var fs = require('fs'); var path = require('path'); var BOSH_DEFAULT_CONFIG_PATH = 'bosh.js.conf'; //コンフィグファイルのパスをさっき作成したものをみるように変更します。 ...省略 var nxb = require("node-xmpp-bosh"); //node-xmpp-boshはパッケージから読み込むように修正します。 //require("./src/main.js");
Procfileを修正します。
run-server.jsを実行するように変更します。
web: node run-server.js
ここまで終わったらHerokuにデプロイしてください。
「heroku logs」コマンドなどで確認すると、動いている事が確認できると思います。
Webのクライアント作成
strophe.jsを公式サイトからゲットします。
http://strophe.im/strophejs/
次のHTMLファイルをstrophe.jsと同じ場所に作成します。
(var BOSH_SERVICEの値は自分のサーバーに変更してください。)
<html> <head> <title>XMPP with JavaScript</title> <script src='http://ajax.googleapis.com/ajax/libs/jquery/1.2.6/jquery.min.js'></script> <script src='strophe.js'></script> <script> var BOSH_SERVICE = 'http://node-xmpp-boshサーバーのアドレス/http-bind/'; var connection = null; function log(msg) { $('#log').append('<div></div>').append(document.createTextNode(msg)); } function onConnect(status) { if (status == Strophe.Status.CONNECTING) { log('Strophe is connecting.'); } else if (status == Strophe.Status.CONNFAIL) { log('Strophe failed to connect.'); $('#connect').get(0).value = 'connect'; } else if (status == Strophe.Status.DISCONNECTING) { log('Strophe is disconnecting.'); } else if (status == Strophe.Status.DISCONNECTED) { log('Strophe is disconnected.'); $('#connect').get(0).value = 'connect'; } else if (status == Strophe.Status.CONNECTED) { log('Strophe is connected.'); log('Send a message to ' + connection.jid + ' to talk to me.'); connection.addHandler(onMessage, null, 'message', null, null, null); connection.send($pres().tree()); } } function onMessage(msg) { var to = msg.getAttribute('to'); var from = msg.getAttribute('from'); var type = msg.getAttribute('type'); var elems = msg.getElementsByTagName('body'); if (type == "chat" && elems.length > 0) { var body = elems[0]; log('I got a message from ' + from + ': ' + Strophe.getText(body)); } // we must return true to keep the handler alive. // returning false would remove it after it finishes. return true; } function sendMessage() { var message = $('#message').get(0).value; var to = $('#to').get(0).value; if(message && to){ var reply = $msg({ to: to, type: 'chat' }) .cnode(Strophe.xmlElement('body', message)); connection.send(reply.tree()); log('I sent ' + to + ': ' + message); } } $(document).ready( function () { connection = new Strophe.Connection(BOSH_SERVICE); // Uncomment the following lines to spy on the wire traffic. //connection.rawInput = function (data) { log('RECV: ' + data); }; //connection.rawOutput = function (data) { log('SEND: ' + data); }; // Uncomment the following line to see all the debug output. //Strophe.log = function (level, msg) { log('LOG: ' + msg); }; $('#connect').bind('click', function () { var button = $('#connect').get(0); if (button.value == 'connect') { button.value = 'disconnect'; var jid = $('#jid').get(0).value; connection.connect(jid, $('#pass').get(0).value, onConnect); } else { button.value = 'connect'; connection.disconnect(); } }); $('#send').bind('click', function () { sendMessage(); }); }); </script> </head> <body> <div id='login' style='text-align: center'> <form name='cred'> <label for='jid'> JID: </label> <input type='text' id='jid'> <label for='pass'> Password: </label> <input type='password' id='pass'> <input type='button' id='connect' value='connect'> </form> </div> <div id='login' style='text-align: center'> <label for='to'> to: </label> <input type='text' id='to'> <label for='message'> message: </label> <input type='text' id='message'> <input type='button' id='send' value='send'> </div> <hr> <div id='log'> </div> </body> </html>