使ってみよう! Windows Live SDK/API

第12回Live Messenger Library ―― オリジナルLive Messengerの作成(中編)

Messenger Library アプリケーションの作成

前回に引き続きWindows Live Messenger Libraryを利用してWebページ上にLive Messengerの機能を実装していきます。はじめに今回作成するアプリケーションを示しておきましょう図1⁠。ユーザ自身や登録しているメンバーの表示、メンバーとのメッセージのやり取りといったメインとなる処理を今回は紹介します。作成するアプリケーションはMessenger Libraryの機能を単純に利用しただけのものになっています。

図1 作成するアプリケーション
図1 作成するアプリケーション

前回に用意した環境と作成したファイルを使って進めていきます。前回はSign-in Controlをホストするところまで行いました。その状態から、ファイルMain.jsにJavaScriptのコードを追記していきます。Sign-in Controlをホストするところまでは前回の記事を参照してください。

Deafult.htmの編集

Default.htmファイルに関しては、新たな情報をいろいろと表示させるため編集します。スタイルシートのファイルも追加しました。まったく同じようにする必要はありませんが、この後に出てくるコードでは各種情報の表示のために以下のDefault.htmに記述したdivタグのid属性値を使用しています。

Default.htm
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Live Messenger Library Sample</title>
<link rel="stylesheet" href="Style.css" type="text/css" /> 
<script type="text/javascript" src="http://settings.messenger.live.com/api/1.0/messenger.js"></script>
<script type="text/javascript" src="Main.js"></script>
</head>
<body onload="Gihyo.LiveSample.pageLoaded();">
<div id="frame">

    <div id="signInFrame"></div>

    <div id="userInfo">
        <div id="displayPicture"></div>
        <div id="status"></div>
        <div id="displayName"></div>
        <div id="personalMessage"></div>
    </div>

    <div>Contact List</div>
    <div id="contacts"></div>

    <div>Active Conversations</div>
    <div id="conversations"></div>
    
    <div><input id="message" type="text" /> <input type="button" value="Send" onclick="Gihyo.LiveSample.sendMessage();" /></div>
    <div id="messages"></div>

</div>
</body>
</html>
Style.css
body {
        font-family: メイリオ;
        font-size: 0.8em;
}       

#frame {
        width: 400px;
}

#signInFrame {
        height: 120px;
}

#userInfo {
        margin: 10px 0 10px 0;
        height: 100px;
}

#displayPicture {
        float: left;
        margin-right: 5px;
}

#contacts, #conversations, #messages {
        margin-bottom: 10px;
        border: solid 1px #ccc;
}

プレゼンスの表示

前回はユーザのアカウントアドレスの表示で終わっていましたので、まずはユーザのプレゼンスを取得して表示してみましょう。プレゼンスには表示名や表示メッセージ、オンラインや退席中などの状態が含まれます。

ユーザのプレゼンスの取得は次の手順で行います。

  1. Userオブジェクト、Presenceプロパティ(IMAddressPresenceクラス型)のPropertyChanged イベントの関連付け
  2. IMAddressPresenceクラスの各プロパティ値の取得

以下のコードは、前回の最後に示したユーザのサインイン完了後に呼ばれるメソッドです。この時点ではUserオブジェクトのPresenceプロパティには欲しい情報は格納されていません。そのため、代わりにユーザのアカウントアドレスを取得していました。

Gihyo.LiveSample.signInCompleted = function(sender, e) {
    if (e.get_resultCode() === Microsoft.Live.Messenger.SignInResultCode.success) {
        var address = Gihyo.LiveSample.User.get_address().get_address();
        $('userInfo').appendChild(document.createTextNode(address));
    }   
}

ユーザアカウントを取得・表示している部分は削除して、次のように書き換えます。PreseceプロパティのPropertyChangedイベントをGihyo.LiveSample.userPresencePropertyChangedメソッドに関連付けます。

// var address = Gihyo.LiveSample.User.get_address().get_address();
// $('userInfo').appendChild(document.createTextNode(address));

Gihyo.LiveSample.User.get_presence().add_propertyChanged(Delegate.create(null, Gihyo.LiveSample.userPresencePropertyChanged));

IMAddressPresenceクラスは次のプロパティを使用します。

  • DisplayName: 表示名
  • PersonalMessage: 表示メッセージ
  • DisplayPictureUrl: 表示アイコンのURLアドレス
  • Status: 状態

DisplayNameとPersonalMessageは文字列、DisplayPictureUrl はMicrosoft.Live.Core.Uriクラス型です。また、StatusはMicrosoft.Live.Messenger.PresenceStatusという列挙体として表されています。

Gihyo.LiveSample.userPresencePropertyChangedメソッドは次のようになります。Statusはコード中のようにMessenger Libraryに用意されているEnum.toStringメソッドを使用すると、数値から文字列へ変換が可能です。ここでは変換した文字列をそのまま表示しています。DisplayPictureUrlは、表示アイコンが設定されていない場合nullとなっているため条件式を入れています。

Gihyo.LiveSample.userPresencePropertyChanged = function(sender, e) {
    var presence = Gihyo.LiveSample.User.get_presence();

    $('displayName').innerHTML = presence.get_displayName();
    $('personalMessage').innerHTML = presence.get_personalMessage();

    var status = presence.get_status();
    $('status').innerHTML = Enum.toString(Microsoft.Live.Messenger.PresenceStatus, status);
    
    if (presence.get_displayPictureUrl() != null) {
        $('displayPicture').innerHTML = '<img src="' + presence.get_displayPictureUrl().toString() + '" />';
    }
}

以上でサインインしたときにユーザのプレゼンス表示ができました。図2に表示した例を示します。プレゼンスが変化すると作成したメソッドが呼び出されるため、Sign-in Controlからサインインした後、別の場所からサインインすると状態がofflineになることがわかります。

図2 ユーザプレゼンスの表示
図2 ユーザプレゼンスの表示

メンバーの表示

ユーザのプレゼンスの表示の次は、ユーザが登録しているメンバーの一覧を表示できるようにしましょう。コンタクトリストとも呼ばれます。手順は次のとおりです。

  1. UserオブジェクトのContactsプロパティ(ContactCollectionクラス型)を参照し、ユーザごとにContactオブジェクトを取得
  2. 各ContactオブジェクトのCurrentAddressプロパティ(IMAddressクラス型)のPropertyChanged イベントの関連付け
  3. Contact.CurrentAddress.Presenceプロパティ(IMAddressPresenceクラス型)の各プロパティ値の取得

メンバーはContactクラスで表されています。メンバーもユーザ同様にIMAddressPresenceオブジェクトがありプレゼンスの取得が可能です。ただし、ユーザがオフラインの場合は取得できません。また表示アイコンの情報は常に取得できないようです。

Contactsプロパティは列挙フィールド(コレクション)として扱えます。GetEnumeratorメソッドにより列挙子を取得して、コレクション内の各アイテムの操作が可能です。列挙子の指す現在のコレクションアイテムはGetCurrentメソッドを使用します。一連の操作は次のコードを参考にしてください。コレクションのGetEnumerator、列挙子のMoveNext、GetCurrentメソッドの組み合わせはこの後もよく出てきます。

Gihyo.LiveSample.signInCompletedメソッド内に処理を追加していきます。すべてのメンバーに対してGihyo.LiveSample.memberPresencePropertyChangedメソッドにPropertyChangedイベントを関連付けています。

var contacts = Gihyo.LiveSample.User.get_contacts().getEnumerator();
while (contacts.moveNext()) {
    var c = contacts.get_current();
    var address = c.get_currentAddress();
    address.get_presence().add_propertyChanged(Delegate.create(null, Gihyo.LiveSample.memberPresencePropertyChanged));
}

Gihyo.LiveSample.memberPresencePropertyChangedメソッドを記述します。ここで実際にメンバー全員のプレゼンスを取得し、ページに表示名と状態をリスト表示します。メンバーがオフラインだった場合は表示名の代わりにContact.CurrentAddress.Addressプロパティからアカウントアドレスを表示することにします。

Gihyo.LiveSample.memberPresencePropertyChanged = function(sender, e) {
    var contacts = Gihyo.LiveSample.User.get_contacts().getEnumerator();
    var sb = new StringBuilder();

    while (contacts.moveNext()) {
        var c = contacts.get_current();
        var address = c.get_currentAddress();
        var presence = address.get_presence();

        var status = presence.get_status();
        var displayName = presence.get_displayName();

        sb.append('<li>');
        sb.append((displayName != '') ? displayName : address.get_address());
        sb.append('(' + Enum.toString(Microsoft.Live.Messenger.PresenceStatus, status) + ')');
        sb.append('</li>');
    }
    $('contacts').innerHTML = '<ul>' + sb.toString() + '</ul>';
}

ここまでを実行すると図3のようになります。メンバーの情報が変更されるたびに表示が更新されると思います。ふたつのアカウントを用いて確認してみてください。

図3 メンバーの表示
図3 メンバーの表示

会話の管理

各メンバーとの会話は、会話ごとにMicrosoft.Live.Messenger.Conversationオブジェクトを用いて管理します。少なくとも以下の処理が必要になります。

  • 会話ごとにConversationオブジェクトの生成(メッセージ受信により新たに会話が発生した場合は自動で作成される)
  • ConversationオブジェクトのSendMessageメソッドによるメッセージの送信
  • ConversationオブジェクトのMessageReceivedイベントにメソッドを関連付け、メッセージ受信の処理

今回作成するアプリケーションでは、現在のアクティブな会話を一覧表示するようにします。メンバーをクリックすると新たに会話を作成し一覧を更新します。またメッセージ受信により新たな会話が発生した場合も一覧を更新します。メッセージの表示は一覧から選択した会話のみページに表示するようにします。メッセージの送信も選択した会話に対して行います。

以上の点について順に説明していきます。

会話の作成とアクティブな会話の一覧表示

まずは、会話の作成とアクティブな会話の一覧を表示できるようにします。

会話の作成

ユーザが会話を新たに作る場合について考えます。会話を表すConversationクラスは、

  • UserオブジェクトのConversationsプロパティ(ConversationCollectionクラス型)のCreateメソッド

により作成できます。Createメソッドの引数には、メンバーのIMAddressオブジェクトを渡します。

今回 作成するアプリケーションでは、ページに表示したメンバー一覧からメンバーをクリックすることで新たに会話を作ります。そこで先ほど書いたメンバー表示の部分を少し変更します。次のようにメンバーをAタグでリンク表示にし、クリックイベントでGihyo.LiveSample.createConversationメソッドを呼び出すようにします。

sb.append('<li>');
sb.append('<a href="#" onclick="Gihyo.LiveSample.createConversation(' +
          "'" + address.get_address() + "'," + address.get_type() + ');return false;">');
sb.append((displayName != '') ? displayName : address.get_address());
sb.append('(' + Enum.toString(Microsoft.Live.Messenger.PresenceStatus, status) + ')');
sb.append('</a>');
sb.append('</li>');

2行追加しました。Conversationオブジェクトを生成するためIMAddressオブジェクトが引数に欲しいところですが、オブジェクトをそのままページに記述できないためアカウントアドレスとアドレスの種類を引数にしています。

Gihyo.LiveSample.createConversationメソッドは次のようになります。ContactsコレクションにはFindメソッドがあり、引数に渡したアドレスとアドレスの種類からContactオブジェクトを取得できます。

Gihyo.LiveSample.createConversation = function(address, type) {
    var contact = Gihyo.LiveSample.User.get_contacts().find(address, type);
    if (contact != null) {
        Gihyo.LiveSample.User.get_conversations().create(contact.get_currentAddress());
    }
}

以上で会話を作成する部分はできました。

アクティブな会話の一覧表示

作成された会話を以下の手順により一覧表示してみましょう。

  1. Userオブジェクト、ConversationsプロパティのCollectionChangedイベントの関連付け
  2. Conversationsプロパティから各Conversationオブジェクトの取得
  3. ConversationオブジェクトのRosterプロパティから会話に参加しているユーザの取得・表示

Conversationsプロパティもコレクションとして扱えます。コレクションの内容が変化したときのイベント、CollectionChangedを関連付けます。Gihyo.LiveSample.signInCompletedメソッド内に以下の記述を追加します。

Gihyo.LiveSample.User.get_conversations().add_collectionChanged(Delegate.create(null, Gihyo.LiveSample.conversationsCollectionChanged));

Conversationには、その会話に参加しているユーザのIMAddressオブジェクトをRosterプロパティによって取得できます。これもコレクションです。ここでは会話に参加しているユーザの表示名をリンクとして表示します。Gihyo.LiveSample.conversationsCollectionChangedメソッドには次のように記述します。

Gihyo.LiveSample.conversationsCollectionChanged = function(sender, e) {
    var userAddress = Gihyo.LiveSample.User.get_address().get_address();
    var sb = new StringBuilder();
    var conversations = Gihyo.LiveSample.User.get_conversations().getEnumerator();
    var index = 0;
    while (conversations.moveNext()) {
        var c = conversations.get_current();

        // 会話に参加しているメンバーを列挙
        var roster = c.get_roster().getEnumerator();
        var names = new Array();

        while (roster.moveNext()) {
            var address = roster.get_current().get_address();
            if (address == userAddress) {
                // ユーザーは除外
                continue;
            }
            
            // ユーザの表示名を取得 空文字だった場合はアドレスを使用
            var displayName = roster.get_current().get_presence().get_displayName();
            if (displayName != '') {
                names.add(displayName);
            } else {
                names.add(address);
            }
        }
        
        sb.append('<li>');
        sb.append('<a href="#" onclick="Gihyo.LiveSample.selectConversation(' + index  + ');return false;">');
        sb.append(names.join(', '));
        sb.append('</a>');
        sb.append(' [<a href="#" onclick="Gihyo.LiveSample.closeConversation(' + index  + ');return false;">close</a>]');
        sb.append('</li>');
        ++index;
    }
    $('conversations').innerHTML = '<ul>' + sb.toString() + '</ul>';
}

この後の処理も考えた記述も含んでいます。会話の選択と終了のためのリンクです。indexという変数を作成してループ中にインクリメントし、コレクションの要素番号を各メソッドの引数としています。これは、ConversationCollectionクラスが要素番号によっても各Conversationオブジェクトにアクセスできるためです。これらのリンクをクリックしたときのメソッドについては後ほど説明します。

ここまでで、メンバーをクリックしたり、メッセージが送信され新しい会話が作成されたりすると、会話の一覧が更新・表示されると思います。実行した結果は図4のようになります。

図4 アクティブな会話の一覧表示
図4 アクティブな会話の一覧表示

メッセージの受信と表示

作成するアプリケーションでは、アクティブな会話一覧から会話を選択し、選択している会話のみページに表示するようにします。また、選択している会話のみメッセージ受信のイベントを関連付け処理するようにします。選択していない会話の受信メッセージはページに表示しないだけで取りこぼすという意味ではありません。

会話一覧から会話を選択したときに呼ばれるメソッドを記述します。手順は次のとおりです。

  1. 選択していた会話のConversationオブジェクトからイベントの関連付けを削除
  2. 新たに選択した会話のConversationオブジェクトのMessageReceivedイベントを関連付け
  3. ConversationオブジェクトのHistoryプロパティからメッセージの履歴を取得・表示

選択したConversationオブジェクトを記憶しておく必要があります。次のようにprototypeオブジェクトを使用してselectedConversationプロパティを作って管理するようにしました。

Gihyo.LiveSample.prototype.selectedConversation = null;

Gihyo.LiveSample.selectConversationメソッドの内容は次のように記述します。Conversation.Historyプロパティには、Conversationオブジェクトが作成されてからのメッセージが格納されています。これを用いて会話を選択したときメッセージ履歴を表示しています。プロパティの型はMessageHistoryクラスです。コレクションとして扱え、メッセージを表すMessageオブジェクトを列挙できます。

Gihyo.LiveSample.selectConversation = function(index) {

    // イベント関連付けの削除
    if (Gihyo.LiveSample.selectedConversation != null) {
        Gihyo.LiveSample.selectedConversation.remove_messageReceived(Gihyo.LiveSample.messageReceived);
    }
    
    // Conversationオブジェクト取得
    var conversation = Gihyo.LiveSample.User.get_conversations().get_item(index);

    // イベント関連付け
    conversation.add_messageReceived(Delegate.create(null, Gihyo.LiveSample.messageReceived));

    Gihyo.LiveSample.selectedConversation = conversation; // 選択したConversationオブジェクトを記憶させておく
    $('messages').innerHTML = ''; // 会話表示欄をクリア

    // 会話の履歴を表示
    var history = conversation.get_history().getEnumerator();
    while (history.moveNext()) {
        Gihyo.LiveSample.displayMessage(history.get_current());
    }
}

続いてMessageReceivedイベントに関連付けたGihyo.LiveSample.messageReceivedメソッドです。イベント引数のオブジェクトからMessageオブジェクト取得し、Gihyo.LiveSample.displayMessageに渡しています。

Gihyo.LiveSample.messageReceived = function(sender, e) {
    Gihyo.LiveSample.displayMessage(e.get_message());
}

最後に会話の履歴表示とメッセージ受信時に呼び出しているGihyo.LiveSample.displayMessageメソッドは次のようになります。

Gihyo.LiveSample.displayMessage = function(message) {
    if (message.get_type() === Microsoft.Live.Messenger.MessageType.textMessage) {
        var span = document.createElement('span');
        var name = message.get_sender().get_presence().get_displayName();
        span.appendChild(document.createTextNode(name + 'の発言: '));
        
        var div = document.createElement('div');
        div.appendChild(span);
        div.appendChild(message.createTextElement());
        $('messages').appendChild(div);
    }
}

受信されるメッセージにはいくつか種類があり、通常のテキストメッセージのときのみ処理するよう条件式を入れています。Messageオブジェクトには、IMAddress クラス型Senderプロパティがあり、送信者の情報が取得できます。また、TypeプロパティがMicrosoft.Live.Messenger.MessageType.textMessageのとき、CreateTextElementメソッドが使用できます。このメソッドは文字色などの修飾を適用したメッセージをspanタグで括られたDOMエレメントとして返します。

以上で受信したメッセージの表示までできるようになりました。アクティブな会話一覧から会話を選択すると、その会話のメッセージが表示されます。ユーザに対してメッセージが送信されると、そのメッセージが追加表示されると思います。別アカウントを利用して試して見ましょう。実行結果を図5に示します。

図5 メッセージの受信・表示
図5 メッセージの受信・表示

メッセージの送信

ここまでくればメッセージの送信は簡単です。

  1. TextMessageオブジェクトの生成
  2. 選択されているConversationオブジェクトのSendMessageメソッドの呼び出し

    以上です。コードは次のようになります。

Gihyo.LiveSample.sendMessage = function() {
    if (Gihyo.LiveSample.selectedConversation != null) {
        var message = new Microsoft.Live.Messenger.TextMessage($('message').value, null);
        Gihyo.LiveSample.selectedConversation.sendMessage(message, null);
        Gihyo.LiveSample.displayMessage(message);
        $('message').value = '';
    }
}

SendMessageメッセージの呼び出しの後、そのメッセージをページ上に表示するためGihyo.LiveSample.displayMessageメソッドの呼び出しもしています。

このメソッドはページ上のボタンのクリックイベントで呼ばれるようにしています。Default.htmを確認してください。

メッセージの送信にはアクティブな会話を選択してから行う必要があります。ページ上では選択している・していないに関わらず何も変化しませんので注意してください。実行した結果は図6のようになります。

図6 メッセージの送信
図6 メッセージの送信

会話の終了

アクティブな会話の一覧には、会話を終了するため[close]というリンクを付けていました。最後にこれを実装しましょう。会話の終了は、ConversationのCloseメソッドを使用します。

Gihyo.LiveSample.closeConversation = function(index) {
    var conversation = Gihyo.LiveSample.User.get_conversations().get_item(index);
    if (Gihyo.LiveSample.selectedConversation == conversation) {
        Gihyo.LiveSample.selectedConversation.remove_messageReceived(Gihyo.LiveSample.messageReceived);
        Gihyo.LiveSample.selectedConversation = null;
        $('messages').innerHTML = '';
    }
    conversation.close();
}

終了した会話が、選択中の会話だった場合を考慮した条件式を入れています。


いかがでしたでしょうか。今回はここまでです。作成したアプリケーションはかなり使いにくいユーザインタフェースでしたが、Live Messengerの機能が実装できることは理解できたのではないかと思います。次回はその他の機能の実装や細かい点について扱う予定です。今回(次回も)の作成したアプリケーションはエラー処理についてほぼ考慮されていません。サインアウトした状態から操作すると簡単にエラーが発生してしまいます。実際にはユーザのプレゼンスの変化によりサインイン中かどうか把握し、処理する必要があります。

おすすめ記事

記事・ニュース一覧