2008/05/05

Google Talk(アプリ版)でグループチャットもどき

昔、UNIXのtalkコマンドっておもしろいなぁって思ってたらIRCってのをみつけて、 ネットワークってスゴいって思った。そっからICQってのが出てきて、MSN Messanger に感動し、Skypeに傾倒して、結局今はもっぱらGoogle Talkを使ってる。 ただ、アプリ版のGoogle Talkはグループチャット機能がなく、ガジェット版はテキスト のコピー&ペーストができない、、、しかたなくチームで作業する時とかは別途でSkype を立ち上げたりしてたのが、PartyChatというグループチャットのゲートウェイ的な サービスを見つけて、なるほどーと思った。 PartyChatは十分素敵に使えるんだけれど、なんかもっと簡単にならないかなぁと思って 車輪の再発明してみた。(Special Thanks to techwalla 汗)
  1. gtgchat@gmail.comをGoogle Talk(もしくはJabberクライアント)に登録するとすぐラウンジ(lounge)に入り、そこでたむろっている人がいれば話すことができる。
  2. 自分の名前を変更したい場合は「name hoge」と入力するとhogeさんになれる。
  3. 「room (好きな部屋の名前)」と入力すると自動的に部屋をつくってそこへ移動でき、同じ名前の部屋に移動した人がいればその中だけで会話ができる。
  4. また、「room」とだけ打つと部屋の一覧が表示され、部屋の名前の先頭に#(半角シャープ)をつけると一覧にでてこない秘密の部屋(例: room #秘密の部屋)ができるので、内緒話ができる。
  5. 「exit」と話しかけると部屋から出る、すなわち終了。
こんだけ。ソースとAmazon EC2上に立ち上げている手順は下記の通り。 ほぼこの用途にしか使わないサーバなので、グループを作成してファイアウォール の設定(72.14.253.125はtalk.google.comのIP)。
# ec2-add-group gtgchat -d "Google Talk GroupChat Server"
GROUP   gtgchat        Google Talk GroupChat Server
# ec2-authorize gtgchat -s 72.14.253.125/32 -p 5222-5222
GROUP           gtgchat
PERMISSION              gtgchat        ALLOWS  tcp     5222    5222    FROM    CIDR    72.14.253.125/32
# もちろん管理用のポートも開けとく
前回作ったAMIで立ち上げる。
# ec2-run-instances -k gsg-keypair -g gtgchat -d "cp /usr/share/zoneinfo/Japan /etc/localtime" ami-eb7c9982
RESERVATION     r-*******      ************    gtgchat
INSTANCE        i-******      ami-eb7c9982                    pending gsg-keypair     0          m1.small 2008-05-04T18:44:08+0000        us-east-1a
# 立ち上がったサーバでphpのインストール
USE='cli pcre unicode xml mhash' emerge php
あとはclass.jabber.phpをとってきて、下記のソースで起動させるだけ(実際にはdaemontoolsで立ち上げてる)。
<?php
require("class.jabber.php");
define('ME','junji.shiokawa@gmail.com'); // 管理者のメアド(最初に認証を済ませておく必要がある)
define('USER','/mnt/supervise/gtgchat/gtgchat.user'); // データの保存ファイル名
$j = new Jabber();
$j->server = 'gmail.com';
$j->connect_server = 'talk.google.com';
$j->port = 5222;
$j->username = 'gtgchat'; // 使用するユーザ名
$j->password = '******'; // パスワード
$j->resource = 'fork'; // リソース名(適当)
$j->log_filename = 'php://stdout';
$j->enable_logging = true;
$j->Connect();
$j->SendAuth();
$j->RosterUpdate();
$j->SendPresence();
chat(ME,'up');
while(1){
    if($j->connected == FALSE){
        $j->Disconnect();
        exit;
    }
    $j->CruiseControl(300);
 }

function Handler_message_chat($p) {
 global $j;
 $e = email($j->GetInfoFromMessageFrom($p));
 $b = body($j->GetInfoFromMessageBody($p));
    $c = explode(' ',$b);
    
 if ( $e == ME && $b == "kill" ) {
        //管理者がkillと入力すると落とせる
        chat($e,'ok');
        $j->Disconnect();
        exit;
    }elseif( $b == 'exit' ) {
        room($e,'');
        chat($e,'ok');
    }elseif( $b == 'help' ) {
        // helpと入力すると使い方を表示
        chat($e,help());
    }elseif( $c[0] == 'name' ) {
        if($c[1]){
            name($e,$c[1]);
            chat($e,'ok');
        }else{
            $u=rooms();
            chat($e,$u[$e]['name'].': '. $e);
        }
    }elseif( $c[0] == 'room' ) {
        if($c[1]){
            $u=room($e,$c[1]);
            $r=array();
            array_push($r,$u[$e]['name']);
            foreach($u as $k => $v){
                if($v['room'] == $c[1] && $k != $e){
                    array_push($r,$v['name']);
                }
            }
            if(count($r)){
                sort($r);
                chat($e,'['.$c[1].'] '.implode(', ',$r));
            }else{
                chat($e,'new');
            }
        }else{
            $u=rooms();
            $a=array();
            foreach($u as $k => $v){
                if(substr($v['room'],0,1)=='#'){
                    if($u[$e]!=$v['room']){
                        continue;
                    }
                }
                if($v['room']){
                    $a[$v['room']]=1;
                }
            }
            if(count($a)){
                $m='';
                ksort($a);
                foreach($a as $k => $v){
                    if($u[$e]['room'] == $k){
                        $m.="*[$k] ";
                    }else{
                        $m.="[$k] ";
                    }
                    $r=array();
                    foreach($u as $uk => $uv){
                        if($uv['room'] == $k){
                            array_push($r,$uv['name']);
                        }
                    }
                    if(count($r)){
                        sort($r);
                        $m.=implode(', ',$r) ."\n";
                    }
                }
                chat($e,$m);
            }else{
                chat($e,'no rooms');
            }
        }
    }else{
        if($b){
            $u=rooms();
            foreach($u as $k => $v){
                if($u[$e]['room'] && $u[$e]['room'] == $u[$k]['room'] && $k!=$e){
                    chat($k,$u[$e]['name'].': '.$b);
                }
            }
        }
    }
    
}

function Handler_presence_subscribe($p) {
    global $j;
    $f = $j->GetInfoFromPresenceFrom($p);
    $j->SubscriptionAcceptRequest($f);
    $j->RosterUpdate();
    name(email($f),email($f));
    room(email($f),'lounge');
}

function Handler_presence_unavailable($p) {
    global $j;
    room(email($j->GetInfoFromPresenceFrom($p)),'');
}

function Handler_message_error($p) {
    room(email($p['message']['@']['from']),'');
}

function chat($e,$m) {
    global $j;
    $j->SendMessage($e, "chat", NULL, array("body" =>$m));
}

function email($f) {
    return strtolower(array_shift(explode('/',$f)));
}

function body ($b) {
    return trim(mb_convert_kana($b,'KVas','UTF-8'));
}

function room($e,$r) {
    $u = unserialize(file_get_contents(USER));
    if($u[$e]['room'] && $u[$e]['room'] != $r){
        foreach($u as $k => $v){
            if($u[$e]['room'] == $u[$k]['room'] && $k!=$e){
                chat($k,'['.$u[$e]['room'].'] - '. $u[$e]['name']);
            }
        }
    }
    if(! $u[$e]['name']) {
        $n1=explode('@',$e);
        $n2=explode('.',$n1[0]);
        if($n2[0]){
            $u[$e]['name']=$n2[0];
        }else{
            $u[$e]['name']=$n1[0];
        }
    }
    if($r){
        $u[$e]['room']=$r;
    }else{
        unset($u[$e]['room']);
    }
    if($u[$e]['room']){
        foreach($u as $k => $v){
            if($u[$e]['room'] == $u[$k]['room'] && $k!=$e){
                chat($k,'['.$u[$e]['room'].'] + '. $u[$e]['name']);
            }
        }
    }
    file_put_contents(USER,serialize($u));
    return $u;
}

function rooms() {
    return unserialize(file_get_contents(USER));
}

function name($e,$a) {
    $u = unserialize(file_get_contents(USER));
    if( strstr($a,'@')) {
        $n1=explode('@',$e);
        $n2=explode('.',$n1[0]);
        if($n2[0]){
            $a=$n2[0];
        }else{
            $a=$n1[0];
        }
    }
    $u[$e]['name']=$a;
    file_put_contents(USER,serialize($u));
    return $u;
}

function help() {
    return "room [name] ... no param lists room and with [name] goto room. [#name] is secret room.
name [nickname] ... set nickname to [nickname].
exit ... leave chat.
";
}
?>

0 コメント: