Notesで書いたメッセージをチャットに送る

前回までに、DominoとNode.jsを同居させ、Node.js上でやり取りされたメッセージをNotesに保存するところまで紹介しました。

今回は、Notesクライアントからのメッセージもチャット上に表示します。最終的には保存されているメッセージをリロードできるようにします。

Notesクライアントで手っ取り早くHTTP送信するには、フォームのPostSaveイベントが使えます。ただし、その場合にはNotesクライアントからDomino+Node.jsへの経路に壁がないことが条件になります。エージェントの保存・更新イベントならその心配はありませんが、今度はリアルタイム性が失われてしまいます。今回はPostSaveイベント方式を使いますが、リアルタイム性があり、通信経路に邪魔が入らなければ、手段は問いません。

Sub Postsave(Source As Notesuidocument)
    Dim xhr As Variant
    Dim session As New NotesSession
    Dim userName As NotesName

    Set userName = New NotesName(session.UserName)

    Set xhr = CreateObject("MSXML2.XMLHTTP")
    xhr.Open "POST", "http://localhost:3000/chat", True
    xhr.SetRequestHeader "Content-Type", "application/json; charset=utf-8"
    xhr.Send |{
        "username": "| & userName.Abbreviated & |",
        "message": "| & Replace(Source.FieldGetText("Subject"), |"|, |\"|) & |"
    }|
End Sub

HTTP通信にはWindowsActiveX経由でMSXML2.XMLHTTPを利用します。送信先localhostですが、Node.jsにたどり着く適切なホスト名を指定します。メッセージの取得元は「Subject」フィールドですが、これは、今回チャットに流用しているディスカッションの仕様によるものなので、データベースとフォームに応じて変更します。

送信側は以上です。次は受信側です。

// routes/chat.js

var express = require('express');
var io;

module.exports = {
  setIo: function(socket_io)
  {
    io = socket_io;
  },
  router: function()
  {
    var router = express.Router();

    /* GET home page. */
    router.post('/', function(req, res)
    {
      io.sockets.emit('chat', req.body);
      res.send('OK');
    });
    return router;
  }
};

新しいルーティングモジュール、routes/chat.jsです。/chatでPOSTされたときに、チャットメンバーにメッセージが送られるようになっています。WebSocketのモジュールを使用するため、setIo関数でモジュールを設定できるようにしています。

今回の主旨に関するソースコードは以上ですが、ルーティングとWebSocketモジュールとの兼ね合いから、他のソースコードの変更を余儀なくされたので、念のため紹介しておきます。

// mySocket.js

var Socket = require('socket.io');
var cookie = require('cookie');
var cookieParser = require('cookie-parser');
var NodeChat = require('./NodeChat');
var io;

module.exports = function(server, sessionStore, User)
{
  io = new Socket(server);

// (中略)

  return io;
};

mySocket.jsでは、初期化されたioを呼び出し元に返すようにしました。これでroutes/chat.js側でWebSocketが使えるようになります。

// app.js

var express = require('express');

// (中略)

// Webアプリケーションサーバー
var app = express();

/**
 * @brief Webアプリケーションサーバーを初期化します。
 * @param session セッション
 * @param sessionStore セッション保管
 * @param passport Passport
 * @param chat Notesからのチャットを受けるルーティング
 */
module.exports = function(session, sessionStore, passport, chat)
{
// (中略)

  // ルーティングを追加します。
  app.use('/', index);
  app.use('/dashboard', dashboard);
  app.use('/login', login(passport));
  app.get('/logout', function(req, res)
  {
    req.logout();
    res.redirect('/');
  });
  app.use('/chat', chat);

// (中略)

  return app;
};

app.jsではさらに引数を増やし、routes/chat.jsが返したルーティングモジュールを引数で受け取り、ルーティングに組み込みます。

// bin/www

// (中略)

/**
 * Webアプリケーションサーバーの初期設定をします。
 */
var chat = require('../routes/chat');
var app = require('../app')(session, sessionStore, auth.passport, chat.router());

var debug = require('debug')('domtest2:server');
var http = require('http');

// (中略)

var server = http.createServer(app);

/**
 * ソケットの初期設定をします。
 */
var io = require('../mySocket')(server, sessionStore, dbInfo.User);
chat.setIo(io);

// (中略)

bin/wwwでは、routes/chat.jsを初期化して、ルーティングへの組み込みとWebSocketの組み込みを行います。app(Webアプリケーション)、server(Webサーバ)、io(WebSocket)の初期化順だと解釈しています。

以上の変更で、以下のような実行結果が得られます。

f:id:takahide-kondoh:20171029142745p:plain

kai shidenさんが「こんにちは」と書いたチャットは前回までと同様です。NotesクライアントからHayato Kobayashiさんが「こんにちは、カイさん。」と返した直後にチャットボードにメッセージが表示されます。通常のWebクライアントであれば、ここはリロードするか、ポーリングするしかないところですね。

次回はいよいよ最終回です。ここまでの仕様では、ブラウザをリロードすると、チャットメッセージは消えてしまいます。リロードしたときに、メッセージ履歴もリロードできるようにして、この連載を終えたいと思います。それでは。