次の方法で共有


メッセージの交換

この「Windows Live Messenger Library チュートリアル」の最終部分では、ユーザーがメンバー リストのどのメンバーとでもオンラインで会話ができるようにアプリケーションを変更します。

実際の例は、http://wlmlhelloworld.mslivelabs.com/Default.htm で参照できます (英語)。

注意   このチュートリアルのサンプル アプリケーションに含まれるその他のファイル (Channel.htm、Privacy.htm、および Default.css) のコードは変更しないでください。

概要

このサンプルでは、直前のトピック「オンライン プレゼンスの管理」から機能を継承し、さらに以下の新しい機能を追加します。

  • Microsoft.Live.Messenger.Conversation のインスタンスの作成
  • Microsoft.Live.Messenger.MessageReceivedEventHandler へのサブスクライブ
  • メッセージの送受信と会話ウィンドウの更新を行うための新しい関数の作成

プロジェクト ファイルの変更

この手順では、Default.htm を更新して、会話ウィンドウ、テキスト入力ボックス、および送信ボタンを作成する HTML を含む <div> 要素 (sendMessage) を追加します。

会話を管理するためのコードを追加するには

  1. IDE またはテキスト エディタを使用して、Default.htm を編集できるように開きます。

  2. signInCompleted を見つけ、この関数の最後に以下の行を追加します (_contactCollection = _user.get_contacts();の直後):

    _user.get_conversations().add_propertyChanged(Delegate.create(null, conversation_collectionChanged));
    

    この行で conversation_collectionChanged イベント ハンドラのデリゲートを作成します。これは、会話のコレクションの変更が発生するたびに呼び出されます (会話が終了したときや新しい会話が開始されたとき)。

  3. setPersonalMessage の直後に、以下の関数を貼り付けます。

    function createConv(index) {
       var newConv = _user.get_conversations().create(_addressList[index]);
       var convId = _user.get_conversations().get_count();
       switchConv(newConv.convId); 
    }
    

    この関数は、選択されたユーザーとの新しい会話を作成します。

  4. displayContacts 内で、以下のブロックを見つけます。

    if (dispName !== '') {
       statusLine = dispName + ' [' + status + ']';
    }
    else {
       statusLine = currAddress + ' [' + status + ']';
    } statusLine = dispName + ' [' + status + ']';
    

    これを、次のように置き換えてください。

    if (dispName !== '') {
       statusLine = '<a href=\'javascript:createConv(' + index + ')\'>' + dispName + ' [' + status + ']</a>';
    }
    else {
       statusLine = '<a href=\'javascript:createConv(' + index + ')\'>' + currAddress + ' [' + status + ']</a>';
    }
    

    このブロックで、createConv を呼び出すリンクをメンバー リストのメンバーごとに追加します。

  5. signInCompleted の直後に、以下のコードを貼り付けます。

    function conversation_collectionChanged(sender,e) {
       displayConversations();
    }
    

    このイベント ハンドラは、会話の一覧の変更が発生したときに Messenger Library によって呼び出されます。

  6. createConv を見つけ、直後に次の 3 つの関数を貼り付けます。

    /* 現在の会話でメッセージを送信します。 */
    function sendMsg() {
       var txtMessage = document.getElementById('txtMessage');
       var messageText = txtMessage.value;
       var message = new Microsoft.Live.Messenger.TextMessage(messageText, null);
       if (_user) {
         _conv.sendMessage(message, null);
       }
       displayMsg(message);
       txtMessage.value = '';
       txtMessage.focus();
    }
    
    /* 新しいメッセージを受信したときに Messenger Library によって呼び出されます。 */
    function recvMsg(sender, e) {
       var message = e.get_message();
       displayMsg(message);
       document.getElementById('msgLastRecv').innerText = '最後のメッセージの受信日時:' + _conv.get_history().get_lastReceived().toString();
    }
    
    /* 会話ウィンドウに、受信メッセージのテキストを表示します。 */
    function displayMsg(message) {
       var elMsg = message.createTextElement();
       var txtConv = document.getElementById('txtConv');
       var userName = "";
       if (message.get_sender().get_presence().get_displayName() != null)
         userName = message.get_sender().get_presence().get_displayName();
    else
         userName = message.get_sender().get_address();
    
    txtConv.appendChild(document.createTextNode(userName + 'の発言: '));
    txtConv.appendChild(elMsg);
    txtConv.appendChild(document.createElement('br'));
    }
    

これらの関数は、メッセージの送信処理、メッセージの受信処理、およびメッセージ送受信時のメッセージ テキストの表示処理を行います。

  1. 次に、以下の関数を貼り付けます。

    function displayConversations() {
       convArray = new Array();
       var sb = new StringBuilder('<p><b>アクティブな会話:(会話をクリックすると再開します):</b></p>');
       var item = 0;
       var enum1 = _user.get_conversations().getEnumerator();
       while (enum1.moveNext()) {
         var c = enum1.get_current();
         convArray.push(c + ":"+ item);
         if (c.get_closed())
           continue;
         sb.append(convLink(c, item));
         sb.append("<hr />");
         item++;
       }
       document.getElementById('divConversations').innerHTML = sb.toString();
    }
    

    この関数は、アクティブな会話の一覧を更新するヘルパ関数です。

  2. displayConversations の後に、以下のコードを貼り付けます。

    function convLink(c, item)
    {
       var roster = c.get_roster();
       var enum1 = roster.getEnumerator();
       var names = new Array();
    
    while (enum1.moveNext())
    {
       var dispName = enum1.get_current().get_presence().get_displayName();
       var dispEmail = enum1.get_current().get_address();
       if (dispName !== '') {
         names.push(dispName);
       } else {
         names.push(dispEmail);
       } 
    }
    
       var sb = new StringBuilder();
       sb.append('<a href=\"javascript:switchConv(' + item + ')\">');
       if (c == _conv)
         sb.append('<b>');
         sb.append(names.join(', '));
         if (c == _conv)
           sb.append('</b>');
           sb.append('</a>');
           sb.append('  ');
           sb.append('<a href=\"javascript:closeConv(' + item + ')\">');
         sb.append('終了</a>');
         return sb.toString();
    }
    

    この convLink 関数は、各会話のリンクを生成します。ユーザーはクリック操作でアクティブな会話を切り替えたり会話を終了したりできます。

  3. 次に、以下の関数を貼り付けます。

    function switchConv(id)
    {
       var c = _user.get_conversations().get_item(id);
       if (c)
       {
       if (_conv) {
         _conv.remove_messageReceived(_convSink);
       }
       _convSink = Delegate.create(null, recvMsg);
       _conv = c;
       _conv.add_messageReceived(_convSink);
       removeChildrenFromNode('txtConv');
       /* 会話の履歴のメッセージをすべて表示します。 */
       var hist = c.get_history();
       var histEnum = hist.getEnumerator();
       while (histEnum.moveNext()) {
         displayMsg(histEnum.get_current());
       }
       document.getElementById('btnSend').disabled = false;
    }
       displayConversations();
       txtMessage.focus();
    }
    

    この switchConv 関数は、アクティブな会話を、ユーザーが選択した会話に切り替えます。

  4. switchConv の後に、以下の最後の関数を貼り付けます。

    /* 現在の会話を終了します。 */
    function closeConv(id) {
       var c = _user.get_conversations().get_item(id);
       convArray.splice(id, 1);
       c.close();
       if (c == _conv)
       {
         removeChildrenFromNode('txtConv');
       }
       displayConversations();
       if(_user.get_conversations().get_count() == 0)
         document.getElementById('btnSend').disabled = true;
    }
    
    /* 指定されたノードからすべての子ノードをクリアします。 
    (つまり、会話ウィンドウからメッセージをクリアします。) */
    function removeChildrenFromNode(id){
       var node = document.getElementById(id);
       if(node == undefined || node == null)
         {
           return;
         }
       var len = node.childNodes.length;
       while (node.hasChildNodes())
       {
         node.removeChild(node.firstChild);
       }
    }
    

    closeConv 関数は、選択された会話を終了します。clearConvWindow は、会話ウィンドウからすべてのテキストを削除します。

  5. スクリプトの一番最後に、以下の宣言を貼り付けます。

    _user = null;
    _signin = null;
    _addressList = null;
    _contactCollection = null;
    _conv = null;
    _convSink = null;
    convArray = null;
    
  6. HTML の本文全体を、以下のコードで置き換えます。

    <body onload="scriptMain()">
       <div id="msgr">
       <table>
       <tr><td>
       <div id="signinframe"></div>
       </td></tr>
       <tr><td>
       <div id="userInfo"></div>
       </td></tr>
       <tr><td>
       <div id="setUserStatus">
          <span><b>状態の変更:</b></span>
          <select id="selectStatus" onchange="selectStatusChanged()">
             <option>オフライン</option>
             <option>退席中</option>
             <option>一時退席中</option>
             <option>取り込み中</option>
             <option>退席中 (自動)</option>
             <option>通話中</option>
            <option>オンライン</option>
             <option>昼休み</option>
          </select>
       </div>
       </td></tr> 
       <tr><td>
       <div id="setPersonalMessage">
       <span><b>表示メッセージ:</b></span> <input id="personalMessage" type="text" />
       <input onclick="setPersonalMessage()" id="btnSetPersonalMessage" type="button" value="設定" />
       </div>
       </td></tr>
       <tr><td>
       <div id="sendMessage">
          <hr />
          <span><b>メッセージの送信:</b></span>
          <p id="contactLabel"></p>
          <p id="msgLastRecv"></p>
          <div id="txtConv"></div><br />
          <input id="txtMessage" type="text"/><br />
          <input onclick="sendMsg()" id="btnSend" type="submit" value="メッセージの送信" disabled="disabled" />
       </div>
       </td></tr>
       <tr><td>
       <div id="divConversations"></div>
       </td></tr>
       <tr><td>
       <div id="divContacts"></div>
       </td></tr> 
       </table>
    
       </div>
       <div id="introPage">
       <h2>Windows Live Messenger Library - Hello, World!</h2>
       <p>これは Windows Live Messenger Library を使用して作成されたデモ アプリケーションです。<br />
       ほとんどすべてのコードがこの Web ページに記述されています。ページを右クリックして [ソースの表示] をクリックすると、このページの作成に使われた HTML および JavaScript を参照できます。</p>
       <h4>このサンプルを使用するには</h4>
       <ol>
          <li>Windows Live ID を使用してサインインします。</li>
          <li>メンバー リストでメンバーを選択します。</li>
          <li>[アクティブな会話] セクションでメンバーの名前をクリックして会話を始めます。複数のアクティブな会話が同時に表示されることがあります。</li>
       </ol>
       </div>
    </body>
    

サンプルを実行するには

  1. Web サーバーにプロジェクト ファイルをアップロードします。

  2. 受信者として使用する Windows Live ID アカウントで Messenger クライアントにサインインします。

  3. Web サイト上の Default.htm にアクセスします。

  4. サンプル アプリケーションで、送信者として使用する Windows Live ID アカウントでサインインします。送信者が受信者のメンバー リストに追加済みのメンバーではない場合、Messenger クライアント側で、その送信者をメンバー リストに追加する必要があります。送信者をメンバー リストに追加すると、クライアントと Web アプリケーションの間の対話を確認できるようになります。

  5. メンバー リストでいずれかのメンバーをクリックして、会話を始めます。送信者と受信者の間でメッセージを送信できます。

完成した Default.htm

以下のコード例は、完成した Default.htm です。

<html xmlns="http://www.w3.org/1999/xhtml" >
<head>
   <title>Windows Live Messenger Library - Hello World!</title>
   <link rel="stylesheet" href="Default.css" type="text/css" />   
   <script src="http://settings.messenger.live.com/api/1.0/messenger.js" type="text/javascript" language="javascript"></script>
   <script type="text/javascript" language="javascript">
     /* これは、スクリプトのメイン関数です。ページのロード時に呼び出されます。 */
     function scriptMain() {
      var privUrl = 'http://' + document.domain + '/Privacy.htm';
      var chanUrl = 'http://' + document.domain + '/Channel.htm';
      _signin = new Microsoft.Live.Messenger.UI.SignInControl('signinframe', privUrl, chanUrl, 'en-US');
       _signin.add_authenticationCompleted(Delegate.create(null, authenticationCompleted));
     }

     /* ユーザーの認証プロセスが完了したときに Messenger Library によって呼び出されます。 */
     function authenticationCompleted(sender, e) {
       _user = new Microsoft.Live.Messenger.User(e.get_identity());
       _user.add_signInCompleted(Delegate.create(null, signInCompleted));
       _user.signIn(null);
     }
     
     /* サインインが完了したときに Messenger Library によって呼び出されます。 */
    function signInCompleted(sender, e) {
      if (e.get_resultCode() === Microsoft.Live.Messenger.SignInResultCode.success) {
       _user.get_presence().add_propertyChanged(Delegate.create(null, user_Presence_PropertyChanged));
       displayUserInfo();
       _addressList = new Array(_user.get_contacts().get_count());
       var enum1 = _user.get_contacts().getEnumerator();
       while (enum1.moveNext()) {
         var c = enum1.get_current();
         var address = c.get_currentAddress();
         address.get_presence().add_propertyChanged(Delegate.create(null, presence_PropertyChanged));
       }
       displayContacts();
       var selectStatus = document.getElementById('selectStatus');
       selectStatus.selectedIndex = 6;
       _contactCollection = _user.get_contacts();
       _user.get_conversations().add_propertyChanged(Delegate.create(null, conversation_collectionChanged)); 
      }
    }
     
     /* 他の場所でサインアウトしたときに Messenger Library によって呼び出されます (ユーザーが別のコンピュータで Messenger にサインインしたとき)。 */
     function onSignedOutRemotely(sender, e) {
       alert('他の場所でサインアウトしました。');
     }
     
    /* 会話の一覧の変更が発生したときに Messenger Library によって呼び出されます。 */
    function conversation_collectionChanged(sender,e) {
      displayConversations();
    }
     
     /* プレゼンス状態の変更が発生したときに Messenger Library によって呼び出されます (メンバーのサインインやサインアウトなど)。 */
     function presence_PropertyChanged(sender, e) {
       displayContacts();
     }
     
     /* ユーザーの状態の変更が発生したときに Messenger Library によって呼び出されます。 */
     function user_Presence_PropertyChanged(sender, e) {
       displayUserInfo();
       if (_user.get_presence().get_status() === Microsoft.Live.Messenger.PresenceStatus.offline) {
         document.getElementById('btnSend').disabled = true;
       }
     }
     
     /* ユーザーがドロップダウン リストで状態を変更したときに呼び出されます。
    Messenger によって、ユーザーの状態プロパティが変更されます。 */
     function selectStatusChanged() {
       var selectStatus = document.getElementById('selectStatus');
       var presenceStatus = [ Microsoft.Live.Messenger.PresenceStatus.appearOffline, Microsoft.Live.Messenger.PresenceStatus.away, Microsoft.Live.Messenger.PresenceStatus.beRightBack, Microsoft.Live.Messenger.PresenceStatus.busy, Microsoft.Live.Messenger.PresenceStatus.idle, Microsoft.Live.Messenger.PresenceStatus.inACall, Microsoft.Live.Messenger.PresenceStatus.online, Microsoft.Live.Messenger.PresenceStatus.outToLunch ];
       if (_user.get_presence().get_status() !== Microsoft.Live.Messenger.PresenceStatus.offline) {
         _user.get_presence().set_status(presenceStatus[selectStatus.selectedIndex]);
       }
     }
     
     /* ユーザー情報の文字列を格納します。 */
     function displayUserInfo() {
      var userInfo = document.getElementById('userInfo');
      var userAddress = _user.get_address().get_address();
       var userDispName = _user.get_presence().get_displayName();
       var userPersonalMessage = _user.get_presence().get_personalMessage();
       var userStatus = Enum.toString(Microsoft.Live.Messenger.PresenceStatus, _user.get_presence().get_status());
      var statusLine = document.createElement('p');
      removeChildrenFromNode('userInfo');      
       if (userDispName !== '') {
       statusLine.appendChild(document.createTextNode(userDispName + ' (' + userAddress + '):' + userStatus));
      }
      else {
       statusLine.appendChild(document.createTextNode(userAddress + ':' + userStatus));
      }
      userInfo.appendChild(statusLine);
       document.getElementById('personalMessage').value = userPersonalMessage;
     }
     
     /* ユーザーのメンバー リストのメンバー全員をループ処理してメンバー情報を表示します。 */
     function displayContacts() {
       var sb = new StringBuilder('<p><b>メンバー リスト (メンバーをクリックすると会話を開始します):</b></p>');
       _addressList = new Array(_user.get_contacts().get_count());
       var enum1 = _user.get_contacts().getEnumerator();
      var index = 0;
       while (enum1.moveNext()) {
         var c = enum1.get_current();
         var address = c.get_currentAddress();
         var dispName = address.get_presence().get_displayName();
         var currAddress = address.get_address();
         var status = Enum.toString(Microsoft.Live.Messenger.PresenceStatus, address.get_presence().get_status());
         var statusLine = '';
         var strDelete = '';
         _addressList[index] = address;
       if (dispName !== '') {
         statusLine = '<a href=\'javascript:createConv(' + index + ')\'>' + dispName + ' [' + status + ']</a>';
       }
       else {
         statusLine = '<a href=\'javascript:createConv(' + index + ')\'>' + currAddress + ' [' + status + ']</a>';
       }
         sb.append(statusLine);
         sb.append('<hr />');
         index++;
       }
       document.getElementById('divContacts').innerHTML = sb.toString();
     }
    
    /* ユーザーの表示メッセージを更新します。 */
     function setPersonalMessage() {
       var personalMessage = document.getElementById('personalMessage');
       var messageText = personalMessage.value;
      messageText = messageText.replace(/</g, "").replace(/>/g, "");
        if (_user) {
         _user.get_presence().set_personalMessage(messageText);
       }
     }
     
     /* 新しい会話を開始します。 */
     function createConv(index) {
       var newConv = _user.get_conversations().create(_addressList[index]);
      var convId = _user.get_conversations().get_count();
      switchConv(newConv.convId); 
     }
     
    /* * 現在の会話でメッセージを送信します。 */
    function sendMsg() {
      var txtMessage = document.getElementById('txtMessage');
      var messageText = txtMessage.value;
      var message = new Microsoft.Live.Messenger.TextMessage(messageText, null);
      if (_user) {
       _conv.sendMessage(message, null);
      }
     displayMsg(message);
      txtMessage.value = '';
      txtMessage.focus();
    }

    /* 新しいメッセージを受信したときに Messenger Library によって呼び出されます。 */
    function recvMsg(sender, e) {
      var message = e.get_message();
      displayMsg(message);
      document.getElementById('msgLastRecv').innerText = '最後のメッセージの受信日時:' + _conv.get_history().get_lastReceived().toString();
    }
    
    /* 会話ウィンドウに、受信メッセージのテキストを表示します。 */
    function displayMsg(message) {
      var elMsg = message.createTextElement();
      var txtConv = document.getElementById('txtConv');
      var userName = "";
      if (message.get_sender().get_presence().get_displayName() != null)
       userName = message.get_sender().get_presence().get_displayName();
      else
       userName = message.get_sender().get_address();
      
      txtConv.appendChild(document.createTextNode(userName + ' の発言: '));
      txtConv.appendChild(elMsg);
      txtConv.appendChild(document.createElement('br'));
    }
    
    /* アクティブな会話をすべてループ処理して、
    会話の参加者の名前を表示します。 */
    function displayConversations() {
      convArray = new Array();
      var sb = new StringBuilder('<p><b>アクティブな会話:(会話をクリックすると再開します):</b></p>');
      var item = 0;
      var enum1 = _user.get_conversations().getEnumerator();
      while (enum1.moveNext()) {
       var c = enum1.get_current();
       convArray.push(c + ":"+ item);
       if (c.get_closed())
         continue;
       sb.append(convLink(c, item));
       sb.append("<hr />");
       item++;
      }
      document.getElementById('divConversations').innerHTML = sb.toString();
    }
    
   /* 会話のリンクを生成します。 */
    function convLink(c, item)
    {
    var roster = c.get_roster();
    var enum1 = roster.getEnumerator();
    var names = new Array();

    while (enum1.moveNext())
    {
      var dispName = enum1.get_current().get_presence().get_displayName();
      var dispEmail = enum1.get_current().get_address();
      if (dispName !== '') {
       names.push(dispName);
      } else {
       names.push(dispEmail);
      }         
    }

    var sb = new StringBuilder();
     sb.append('<a href=\"javascript:switchConv(' + item + ')\">');
    if (c == _conv)
      sb.append('<b>');
    sb.append(names.join(', '));
    if (c == _conv)
      sb.append('</b>');
    sb.append('</a>');
    sb.append('  ');
    sb.append('<a href=\"javascript:closeConv(' + item + ')\">');
    sb.append('終了</a>');
    return sb.toString();
    }
     
    /* アクティブな会話を切り替えます。 */
    function switchConv(id)
    {
      var c = _user.get_conversations().get_item(id);
      if (c)
      {
       if (_conv) {
         _conv.remove_messageReceived(_convSink);
       }
       _convSink = Delegate.create(null, recvMsg);
       _conv = c;
       _conv.add_messageReceived(_convSink);
       
       removeChildrenFromNode('txtConv');
       
       /* 会話の履歴のメッセージをすべて表示します。 */
       var hist = c.get_history();
       var histEnum = hist.getEnumerator();
       while (histEnum.moveNext()) {
         displayMsg(histEnum.get_current());
       }
         
       document.getElementById('btnSend').disabled = false;
      }
      displayConversations();
      txtMessage.focus();
    }
     
    /* 現在の会話を終了します。 */
    function closeConv(id) {
      var c = _user.get_conversations().get_item(id);
      convArray.splice(id, 1);
      c.close();
      if (c == _conv)
      {
       removeChildrenFromNode('txtConv');
      }
      displayConversations();
      if(_user.get_conversations().get_count() == 0)
       document.getElementById('btnSend').disabled = true;
    }

    /* 指定されたノードからすべての子ノードをクリアします。 
    (つまり、会話ウィンドウからメッセージをクリアします)。 */
    function removeChildrenFromNode(id){
      var node = document.getElementById(id);
      if(node == undefined || node == null)
       {
         return;
       }
      var len = node.childNodes.length;
      while (node.hasChildNodes())
      {
       node.removeChild(node.firstChild);
      }
    }
    
    _user = null;
    _signin = null;
    _addressList = null;
    _contactCollection = null;
    _conv = null;
    _convSink = null;
    convArray = null;
   </script>
</head>
<body onload="scriptMain()">
   <div id="msgr">
   <table>
   <tr><td>
   <div id="signinframe"></div>
   </td></tr>
   <tr><td>
   <div id="userInfo"></div>
   </td></tr>
   <tr><td>
   <div id="setUserStatus">
     <span><b>状態の変更:</b></span>
     <select id="selectStatus" onchange="selectStatusChanged()">
       <option>オフライン</option>
       <option>退席中</option>
       <option>一時退席中</option>
       <option>取り込み中</option>
       <option>退席中 (自動)</option>
       <option>通話中</option>
       <option>オンライン</option>
       <option>昼休み</option>
     </select>
   </div>
   </td></tr> 
   <tr><td>
   <div id="setPersonalMessage">
   <span><b>表示メッセージ:</b></span> <input id="personalMessage" type="text" />
   <input onclick="setPersonalMessage()" id="btnSetPersonalMessage" type="button" value="設定" />
   </div>
   </td></tr>
   <tr><td>
   <div id="sendMessage">
     <hr />
     <span><b>メッセージの送信:</b></span>
     <p id="contactLabel"></p>
     <p id="msgLastRecv"></p>
     <div id="txtConv"></div><br />
     <input id="txtMessage" type="text"/><br />
     <input onclick="sendMsg()" id="btnSend" type="submit" value="メッセージの送信" disabled="disabled" />
   </div>
   </td></tr>
   <tr><td>
   <div id="divConversations"></div>
   </td></tr>
   <tr><td>
   <div id="divContacts"></div>
   </td></tr> 
   </table>

   </div>
   <div id="introPage">
   <h2>Windows Live Messenger Library - Hello, World!</h2>
   <p>これは Windows Live Messenger Library を使用して作成されたデモ アプリケーションです。<br />
   ほとんどすべてのコードがこの Web ページに記述されています。ページを右クリックして [ソースの表示] をクリックすると、このページの作成に使われた HTML および JavaScript を参照できます。</p>
   <h4>このサンプルを使用するには</h4>
   <ol>
     <li>Windows Live ID を使用してサインインします。</li>
     <li>メンバー リストでメンバーを選択します。</li>
     <li>[アクティブな会話] セクションでメンバーの名前をクリックして会話を始めます。複数のアクティブな会話が同時に表示されることがあります。</li>
   </ol>
   </div>
</body>
</html>

関連項目

概念

Windows Live Messenger Library チュートリアル
方法: オンライン プレゼンスの監視
方法: 会話の管理