Node.jsの勉強がてら地図上でお話しできるチャットアプリを作成していたら、チャットルームの管理が面倒なので、FacebookのMessengerで管理しているグループと連動できないかと思ってやってみた雑記です。
FacebookメッセンジャーのConversation IDを利用してチャットルームUIを作るの続編。
チャットルーム ロジック実装
socket.ioのRoomsを使ったチャットルームの作り方
チャットルームはSocket.ioのRoomsを使って実現します。RoomsはSocketのチャンネルであり、一度Roomsでチャンネルを初期化してしまえば、Socket.ioでチャンネルごとに送信先を分けることができます。
- socket.io: Rooms
io.js(Room実装前)
var socketio = require('socket.io');
function io(server) {
var io = socketio.listen(server);
io.on('connection', function (socket) {
socket.on('chat message', function(msg) {
io.emit('chat message', msg);
});
socket.on('map message', function(msg) {
io.emit('map message', msg);
});
});
}
module.exports = io;
上記コード内に以下のコードを追加してRoomを初期化。
socket.join('<Room ID>');
メッセージ配信時に特定のRoomにのみ配信を行うためにRoom ID指定したioでemitする。
io.to('<Room ID>').emit('chat message', msg);
これだけ。
以下、Room実装後のio.js。function io内抜粋。
io.js(Room実装後)
var io = socketio.listen(server);
var store = {};
io.on('connection', function (socket) {
socket.on('join', function(msg) {
usrobj = {
'room': msg.roomid,
'name': msg.name
};
store[msg.id] = usrobj;
socket.join(msg.roomid);
});
socket.on('chat message', function(msg) {
io.to(store[msg.id].room).emit('chat message', msg);
});
socket.on('map message', function(msg) {
io.to(store[msg.id].room).emit('map message', msg);
});
});
この後はおまけです。
チャットルームを永続的に残さない、かつユーザー1人に対して参加できるチャットルームを1ルームのみとして、FacebookのMessengerで管理されているグループと同期してチャットルームを生成する仕組みです。
チャットルームUI実装で取得したFacebookのConversation IDをRoom IDに使用して実装
ルームに入る際にルーム初期化のための送信メッセージ('join')をemitする。
roomidにConversation IDが入ります。
Conversation IDの取得はFacebookメッセンジャーのConversation IDを利用してチャットルームUIを作るを参考にしてください。
socket.emit('join', {
roomid: e.target.id,
name: myname,
id: myid
});
socket.on('join')で渡されたmsg.idにはFacebookのConversation IDが入ってるので、これをRoom IDとしてsocket.joinする。
socket.join(msg.roomid);
これにより、RoomはFacebookのメッセンジャーグループと同期される。
連想配列(store = {})を作って、そこに接続ユーザーの情報を格納して、emitする際にstoreからRoom IDを取得して利用する。
io.to(store[msg.id].room).emit('chat message', msg);
退出の実装
最後に、Socket切断時の自動退出の実装です。
socket.idとFacebookユーザーIDを紐付けて管理します。
var idstore = {};
// JOIN時に格納
idstore[socket.id] = { 'id': msg.id };
Webアプリなのでセッションが切れると接続も切れる。
(※今回は明示的に切断するためのUIは作ってない)
Socketのdisconnect時にidstore[socket.id].idで退出したユーザーを確定する。退出メッセージ送信後は不要なidstoreを削除。
socket.on('disconnect', function() {
if (idstore[socket.id]) {
var _roomid = store[idstore[socket.id].id].room;
socket.leave(_roomid);
io.to(_roomid).emit('chat message', {
id: idstore[socket.id].id,
name: store[idstore[socket.id].id].name,
text: '退出!'
});
delete idstore[socket.id];
}
});
メモ:if (idstore[socket.id])内で実行するようにしておかないとサーバー側のエラーが出てHerokuで動作させているアプリがエラーで使えなくなった。ログを見てると切断後ポーリングをして接続状況を確認してるのか、再度disconnectが発行されて処理に失敗してる感じ。
以上です。