他のことをしていても投稿に気づきやすいチャット作ってみた

sinatra + websocket + webkitNotificationのなせる技。
まあ、ただのチャットですが、投稿したときにNotificationの機能を使って、ブラウザの状態に関係なくメッセージが見れるようになっています。
一応、ブログ用に必要最小限の要素にまとめたつもりです。


古橋さんのページを参考にしています。
http://d.hatena.ne.jp/viver/20100717/p1
今回はwebsocketを簡単に使うためにrev-websocketを利用させて頂いてます。


必要なパッケージをインストールしておきます。
gem install sinatra
gem install rev-websocket

あと、実行手順は

  1. ruby main_server.rbでサーバーを起動
  2. chromelocalhost:8080にアクセス
  3. Notificationを許可して、テキストボックスに何か打ち込めば以下の写真のようになるはず。Notificationはブラウザを最小化してても表示されるので、仕事中のコミュニケーションとかに使えそうです?


必要なコードは以下の3つです。

main_server.rb

#!/usr/bin/env ruby

fork {
        load 'chat_server.rb'
        exit 0
}

require 'rubygems'
require 'sinatra'

get '/' do
        erb :index
end

set :port, 8080

chat_server.rb

require 'rubygems'
require 'rev/websocket' 

$sockets = {}

# Rev::WebSocketを継承したクラスを作成
class MyConnection < Rev::WebSocket
        # 接続されたら呼ばれる
        def on_open
                # 接続先に追加
                $sockets[self] = self
                # 全員に通知
                $sockets.each_key do |sock|
                        sock.send_message("join in #{peeraddr[2]}")
                end
        end

        # メッセージが届いたら呼ばれる
        def on_message(data)
                $sockets.each_key do |sock|
                        sock.send_message(data)
                end
        end

        # 切断されたら呼ばれる
        def on_close
                # 接続先から削除
                $sockets.delete(self)
                $sockets.each_key do |sock|
                        sock.send_message("break away #{peeraddr[2]}")
                end
        end
end

ws = Rev::WebSocketServer.new('0.0.0.0', 8081, MyConnection)
ws.attach(Rev::Loop.default)

Rev::Loop.default.run

views/index.html

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta content="text/css" http-equiv="content-style-type" />
<meta content="text/javascript" http-equiv="content-script-type" />

<title>Chat with notification</title>

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
<script>
WS_URL = "ws://localhost:8081";
SHOW_TIME = 5000;
var global = this;

$(document).ready(function(){
        function show_message(message) {
                str = "<p><span class='time'>"+new Date()+"</span> "+message+"</p>"
                $("#message").append(str);
        }

        show_message("connecting to "+WS_URL+"...");
        ws = new WebSocket(WS_URL);

        ws.onopen = function() {
                show_message("connected.");
        }

        ws.onclose = function() {
                show_message("disconnected...");
        }

        ws.onerror = function(msg) {
                show_message("failed to connect:"+msg);
        }

        ws.onmessage = function(event) {
                show_message(event.data);

                if(!window.webkitNotifications.checkPermission()) {
                        var notify = window.webkitNotifications;
                        var popup = notify.createNotification("", "Pushed Message", event.data);
                        popup.show();
                        setTimeout(function() {
                                popup.cancel();
                        }, SHOW_TIME);
                } else {
                        if(window.webkitNotifications) {
                                show_message("failed to notify.");
                        }
                }
        }

        // Notification利用の許可
        $("#request-permission").click(function(e) {
                e.preventDefault();
                if(window.webkitNotifications.checkPermission()) {
                        window.webkitNotifications.requestPermission(function() {
                                if(cd) {
                                        cd(window.webkitNotifications.checkPermission() == 0);
                                }
                        });
                }
        });
});
</script>
</head>

<body>
        <a href="#" class="submit" id="request-permission">WebNotificationによる通知許可</a><br>

        <input id="input" type="text" onChange="javascript:ws.send(this.value);this.value='';"/>
        メッセージを入力してEnterで投稿<br>

        <pre>
                <div id="message"></div>
        </pre>
</body>


古橋さんの作ったMessagePack-RPCとかを使って、オンラインゲームっぽいもの作ったり何だりしてますが、それは追々まとめながら投稿したいと思います・・・