メッセージの送受信

会話エージェントはメッセージングを通じてユーザーと通信し、シームレスな対話を可能にします。 テキストまたは音声による操作を通じて、ユーザーとの実際の会話をシミュレートできます。 エージェントの会話が対話型、動的、アダプティブ、およびユーザー フレンドリであることを確認する必要があります。

メッセージの内容

エージェントとユーザーの間のメッセージ操作には、次のようなさまざまな種類のメッセージ コンテンツを含めることができます。

コンテンツ タイプ ユーザーからエージェントへ エージェントからユーザーへ
リッチ テキストと絵文字 ✔️ ✔️
画像 ✔️ ✔️
アダプティブ カード ✔️

リッチ テキスト メッセージと絵文字を使用する

Teams エージェントは、リッチ テキストと絵文字を送信できます。 Teams では、U+1F600 のように UTF-16 を介して絵文字がサポートされ、笑う顔に対応しています。

画像メッセージを使用する

エージェント メッセージをポップするには、画像を添付ファイルとして追加します。

  • 画像は最大 1024 × 1024 ピクセル、PNG、JPEG、GIF 形式では 1 MB です。 アニメーション GIF はサポートされていません。

  • XML を使用して、各イメージの高さと幅を指定できます。 Markdown では、イメージ サイズの既定値は 256×256 です。 例:

    • ✔️ : <img src="http://aka.ms/Fo983c" alt="Duck on a rock" height="150" width="223"></img>
    • ❌: ![Duck on a rock](http://aka.ms/Fo983c).

添付ファイルの詳細については、「メッセージに メディア添付ファイルを追加する」を参照してください。

アダプティブ カードを使用する

会話エージェントには、ビジネス ワークフローを簡略化するアダプティブ カードを含めることができます。 アダプティブ カードは、カスタマイズ可能な豊富なテキスト、音声、画像、ボタン、入力フィールドを提供します。 エージェントでアダプティブ カードを作成し、Teams、Web サイトなどの複数のアプリに表示できます。

詳細については、以下を参照してください:

次のコードは、単純なアダプティブ カードを送信する例を示しています。

例: 単純なアダプティブ カードを送信する
{
    "type": "AdaptiveCard",
    "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
    "version": "1.5",
    "body": [
    {
        "items": [
        {
            "size": "large",
            "text": "Simple Adaptive Card example with a Textbox",
            "type": "TextBlock",
            "weight": "bolder",
            "wrap": true
        },
        ],
        "spacing": "extraLarge",
        "type": "Container",
        "verticalContentAlignment": "center"
    }
    ]
}

メッセージの送受信

メッセージの送受信は、エージェントのコア機能です。

チャットでは、各メッセージは messageType: message 型のActivity オブジェクトです。 誰かがメッセージを送信すると、Microsoft Teamsはエージェントに投稿します。 Teams は JSON オブジェクトをエージェントのメッセージング エンドポイントに送信し、メッセージングに使用できるエンドポイントは 1 つだけです。 その後、エージェントはメッセージをチェックして、その種類を特定し、それに応じて応答します。

基本的な会話は、1 つの REST API である Teams SDK Framework コネクタを介して管理されます。 この API を使用すると、エージェントが Teams やその他のチャネルと通信できるようになります。 Bot Builder SDK には、次の機能があります。

  • Teams SDK Framework コネクタに簡単にアクセスできます。
  • 会話フローと状態を管理するためのツール。
  • 自然言語処理 (NLP) などのコグニティブ サービスを追加する簡単な方法。

エージェントは、 Text プロパティを使用して Teams からメッセージを取得し、1 つまたは複数の応答をユーザーに送信できます。

詳細については、「 エージェント メッセージのユーザー属性」を参照してください。

次の表は、エージェントが受け取ってアクションを実行できるアクティビティの一覧です。

メッセージの種類 ペイロード オブジェクト 範囲
メッセージ アクティビティを受信する メッセージ アクティビティ すべて
メッセージの編集アクティビティを受信する メッセージ編集アクティビティ すべて
メッセージの削除を取り消すアクティビティを受信する メッセージの削除の取り消しアクティビティ すべて
論理的な削除メッセージ アクティビティを受信する メッセージの論理的な削除アクティビティ すべて

メッセージ アクティビティを受信する

テキスト メッセージを受信するには、Activity オブジェクトの Text プロパティを使用します。 エージェントのアクティビティ ハンドラーで、ターン コンテキスト オブジェクトの Activity を使用して、1 つのメッセージ要求を読み取ります。

次のコードは、メッセージ アクティビティを受信する例を示しています。

app.OnMessage(async context =>
{
    await context.Send($"Echo: {context.Activity.Text}");
});

app.on('message', async ({ activity, send }) => {
    await send(`Echo: '${activity.text}'`);
});

@app.on_message
async def handle_message(ctx: ActivityContext[MessageActivity]):
    await ctx.send(f"Echo: {ctx.activity.text}")

{
    "type": "message",
    "id": "1485983408511",
    "timestamp": "2017-02-01T21:10:07.437Z",
    "localTimestamp": "2017-02-01T14:10:07.437-07:00",
    "serviceUrl": "https://smba.trafficmanager.net/amer/",
    "channelId": "msteams",
    "from": {
        "id": "29:1XJKJMvc5GBtc2JwZq0oj8tHZmzrQgFmB39ATiQWA85gQtHieVkKilBZ9XHoq9j7Zaqt7CZ-NJWi7me2kHTL3Bw",
        "name": "Megan Bowen",
        "aadObjectId": "7faf8ab2-3d56-4244-b585-20c8a42ed2b8"
    },
    "conversation": {
        "conversationType": "personal",
        "id": "a:17I0kl9EkpE1O9PH5TWrzrLNwnWWcfrU7QZjKR0WSfOpzbfcAg2IaydGElSo10tVr4C7Fc6GtieTJX663WuJCc1uA83n4CSrHSgGBj5XNYLcVlJAs2ZX8DbYBPck201w-"
    },
    "recipient": {
        "id": "28:c9e8c047-2a74-40a2-b28a-b162d5f5327c",
        "name": "Teams TestAgent"
    },
    "textFormat": "plain",
    "text": "Hello Teams TestAgent.Sending bold-italic rich text",
    "attachments": [
      {
            "contentType": "text/html",
            "content": "<div><div>Hello Teams TestAgent. Sending <strong>bold</strong>-<em>italic</em> rich text.</div>\n</div>"
      } 
    ],
    "entities": [
      { 
        "locale": "en-US",
        "country": "US",
        "platform": "Windows",
        "timezone": "America/Los_Angeles",
        "type": "clientInfo"
      }
    ],
    "channelData": {
        "tenant": {
            "id": "72f988bf-86f1-41af-91ab-2d7cd011db47"
        }
    },
    "locale": "en-US"
}

開封確認メッセージを受信する

Teams の [開封確認] 設定を使用すると、1 対 1 のチャットとグループ チャットで受信者がメッセージを読んだときに、チャット メッセージの送信者に通知を受け取ります。 受信者がメッセージを読み取った後、メッセージの横に [表示 が表示されます。 また、[開封確認] 設定を使用して開封確認イベントを受信するようにエージェント を構成することもできます 。 開封確認イベントは、次の方法でユーザー エクスペリエンスを向上させるのに役立ちます。

  • アプリ ユーザーが個人用チャットでメッセージを読み取っていない場合は、フォローアップ メッセージを送信するようにエージェントを構成できます。

  • 読み取りレシートを使用して、エージェントのエクスペリエンスを調整するフィードバック ループを作成できます。

注:

  • 開封確認は、ユーザーからエージェントへのチャット シナリオでのみサポートされます。
  • エージェントの開封確認は、チーム、チャネル、グループ チャットのスコープをサポートしていません。
  • 管理者またはユーザーが [開封確認 ] 設定を無効にした場合、エージェントは開封確認イベントを受け取りません。

エージェントの開封確認イベントを受信するには、次のことを確認します。

  • 次のように、 RSCChatMessageReadReceipt.Read.Chat アクセス許可を アプリ マニフェストに追加します。

    
    "webApplicationInfo": {
    
         "id": "38f0ca43-1c38-4c39-8097e-47f62c686500",
         "resource": ""
    },
    "authorization": {
        "permissions": {
        "orgwide": [],
         "resourceSpecific": [
            {
            "name": "ChatMessageReadReceipt.Read.Chat",
            "type": "Application"
            }
            ]
         }
     }
    
    

Graph APIを使用して RSC アクセス許可を追加することもできます。 詳細については、consentedPermissionSetを参照してください。

  • メソッド OnReadReceiptcontext.Activity.Value.LastReadMessageId でオーバーライドします。

    context.Activity.Value.LastReadMessageIdメソッドは、メッセージが受信者によって読み取られたかどうかを判断するのに役立ちます。 compareMessageIdLastReadMessageId以下の場合は、メッセージが読み取られます。 OnReadReceipt メソッドをオーバーライドして、context.Activity.Value.LastReadMessageId メソッドを使用して開封確認を受け取ります。

    app.OnReadReceipt(async context =>
    
    {
        var lastReadMessageId = context.Activity.Value.LastReadMessageId;
        await context.Send("User read the agent's message");
    });
    

次の例は、エージェントが受け取る開封確認イベント要求を示しています。

    {
        "name": "application/vnd.microsoft.readReceipt",
        "type": "event",
        "timestamp": "2023-08-16T17:23:11.1366686Z",
        "id": "f:b4783e72-9d7b-2ed9-ccef-ab446c873007",
        "channelId": "msteams",
        "serviceUrl": "https://smba.trafficmanager.net/amer/",
        "from": {
            "id": "29:1-8Iuh70W9pRqV8tQK8o2nVjxz33RRGDKLf4Bh7gKnrzN8s7e4vCyrFwjkPbTCX_Co8c4aXwWvq3RBLr-WkkVMw",
            "aadObjectId": "5b649834-7412-4cce-9e69-176e95a394f5"
        },
        "conversation": {
            "conversationType": "personal",
            "tenantId": "6babcaad-604b-40ac-a9d7-9fd97c0b779f",
            "id": "a:1xlimp68NSUxEqK0ap2rXuwC9ITauHgV2M4RaDPkeRhV8qMaFn-RyilMZ62YiVdqs8pp43yQaRKvv_U2S2gOS5nM-y_pOxVe4BW1qMGPtqD0Bv3pw-nJXF0zhDlZHMZ1Z"
        },
        "recipient": {
            "id": "28:9901a8b6-4fef-428b-80b1-ddb59361adeb",
            "name": "Test Agent"
        },
        "channelData": {
            "tenant": {
                "id": "6babcaad-604b-40ac-a9d7-9fd97c0b779f"
            }
        },
        "value": {
            "lastReadMessageId": "1692206589131"
        }
    }
    
  • エージェントが開封確認イベントを受信するためのテナントに対して、開封確認 管理者の設定 または ユーザー設定 が有効になっています。 管理者またはユーザーは、開封確認の設定を有効または無効にする必要があります。

ユーザーからエージェントへのチャット シナリオでエージェントが有効になると、エージェントは、ユーザーがエージェントのメッセージを読み取ったときに、すぐに開封確認イベントを受信します。 イベントの数をカウントすることでユーザー エンゲージメントを追跡でき、コンテキスト対応メッセージを送信することもできます。

メッセージの編集アクティビティを受信する

メッセージを編集すると、エージェントはメッセージの編集アクティビティの通知を受け取ります。

エージェントでメッセージ アクティビティの編集通知を取得するには、ハンドラー OnMessageEdit オーバーライドできます。

送信されたメッセージが編集されたときに OnMessageEdit を使用したメッセージの編集アクティビティ通知の例を次に示します。

app.OnMessageEdit(async context =>
{
    await context.Send("message is updated");
}); 
app.on('messageEdit', async ({ activity, send }) => {
    const editedMessage = activity.text;
    await send(`The edited message is ${editedMessage}`);
});
{
"type":"messageUpdate",
"timestamp":"2022-10-28T17:19:39.4615413Z",
"localTimestamp":"2022-10-28T10:19:39.4615413-07:00",
"id":"1666977568748",
"channelId":"msteams",
"serviceUrl":"https://canary.botapi.skype.com/amer/",
"from": {
    "id":"29:1BLjP9j3_PM4mubmQZsYPx7jDyLeLf_YVA9sVPV08KMAFMjJWB_EUGveb9EVDh9TslNp9qjnzEBy3kgw01Jf1Kg",
    "name":"Mike Wilber",
    "aadObjectId":"520e4d1e-2108-43ee-a092-46a9507c6200"caching
},
"conversation":{
    "conversationType":"personal",
    "tenantId":"528dbe3f-15e0-4e37-84a1-00cc305847dd","id":"a:1pweuGJ44RkB90tiJNQ_I6g3vyuP4CYA_f-v6f0Vd-Bs3Ce85C73Ah1y8TvyjESsTHWjjgw-gnsuIuCUOWkfOCq6qaUYsk2_-fj93XXXHUMAUzhFFvTnaCU7V4WiMqRPB"
},
"recipient":{
    "id":"28:0d569679-gb4j-479a-b0d8-238b6e6b1149",
    "name":"TestAgent"
},
"entities":[
    {
        "locale":"en-US",
        "country":"US",
        "platform":"Web",
        "timezone":"America/Los_Angeles",
        "type":"clientInfo"
    }
],
"channelData":{
    "eventType":"editMessage",
    "tenant":{"id":"528dbe3f-15e0-4e37-84a1-00cc305847dd"}
},
"locale":"en-US",
"localTimezone":"America/Los_Angeles"
}  
PUT {Service URL of your agent}/v3/conversations/{conversationId}/activities/{activityId}
{
    "type": "message",
    "text": "This message has been updated"
}

メッセージを送信する

テキスト メッセージを送信するには、アクティビティとして送信する文字列を指定します。 エージェントのアクティビティ ハンドラーで、ターン コンテキスト オブジェクトの context.Send(...) メソッドを使用して、1 つのメッセージ応答を送信します。 オブジェクトの multiple context.Send(...) calls メソッドを使用して、複数の応答を送信します。

次のコードは、ユーザーが会話に追加されたときにメッセージを送信する例を示しています。

app.OnMembersAdded(async context =>
{
    foreach (var member in context.Activity.MembersAdded)
    {
        if (member.Id != context.Activity.Recipient.Id)
        {
            await context.Send("Hello and welcome!");
        }
    }
});
   app.on('membersAdded', async ({ activity, send }) => {
    for (const member of activity.membersAdded ?? []) {
        if (member.id !== activity.recipient.id) {
            await send(`Welcome to the team ${member.name}`);
        }
    }
});
@app.on_members_added
async def handle_members_added(ctx: ActivityContext):
    for member in ctx.activity.members_added:
        if member.id != ctx.activity.recipient.id:
            await ctx.send(f"Welcome your new team member {member.id}")
{
    "type": "message",
    "from": {
        "id": "28:c9e8c047-2a34-40a1-b28a-b162d5f5327c",
        "name": "Teams TestAgent"
    },
    "conversation": {
        "id": "a:17I0kl8EkpE1O9PH5TWrzrLNwnWWcfrU7QZjKR0WSfOpzbfcAg2IaydGElSo10tVr4C7Fc6GtieTJX663WuJCc1uA83n4CSrHSgGBj5XNYLcVlJAs2ZX8DbYBPck201w-",
        "name": "Convo1"
   },
   "recipient": {
        "id": "29:1XJKJMvc5GBtc2JwZq0oj8tHZmzrQgFmB25ATiQWA85gQtHieVkKilBZ9XHoq9j7Zaqt7CZ-NJWi7me2kHTL3Bw",
        "name": "Megan Bowen"
    },
    "text": "My agent's reply",
    "replyToId": "1632474074231"
}
HTTP Request: {Service URL of your agent}/v3/conversations/{conversationId}/activities
{
    "type": "message",
    "from": {
        "id": "28:c9e8c047-2a34-40a1-b28a-b162d5f5327c",
        "name": "Teams TestAgent"
    },
    "conversation": {
        "id":"a:17I0kl8EkpE1O9PH5TWrzrLNwnWWcfrU7QZjKR0WSfOpzbfcAg2IaydGElSo10tVr4C7Fc6GtieTJX663WuJCc1uA83n4CSrHSgGBj5XNYLcVlJAs2ZX8DbYBPck201w-",
        "name": "Convo1"
    },
    "recipient": {
        "id": "29:1XJKJMvc5GBtc2JwZq0oj8tHZmzrQgFmB25ATiQWA85gQtHieVkKilBZ9XHoq9j7Zaqt7CZ-NJWi7me2kHTL3Bw",
        "name": "Megan Bowen"
    },
    "text": "My agent's reply"
}

注:

  • メッセージ分割は、テキスト メッセージと添付ファイルが同じアクティビティ ペイロードで送信されるときに発生します。 Teams では、このアクティビティを 2 つの別々のアクティビティに分割します。1 つはテキスト メッセージで、もう 1 つは添付ファイルを含みます。 アクティビティが分割されると、応答としてメッセージ ID は受信されません。これは、メッセージを事前に 更新または削除 するために使用されます。 メッセージ分割に応じてではなく、個別のアクティビティを送信することをお勧めします。
  • 送信されたメッセージは、パーソナル化を提供するためにローカライズできます。 詳細については、「 アプリのローカライズ」を参照してください。

ユーザーとエージェントの間で送信されるメッセージには、メッセージ内の内部チャネル データが含まれます。 このデータを使用すると、エージェントはそのチャネルで適切に通信できます。 Bot Builder SDK を使用すると、メッセージ構造を変更できます。

メッセージの削除を取り消すアクティビティを受信する

メッセージの削除を取り消すと、エージェントはメッセージの取り消しアクティビティの通知を受け取ります。

エージェントで削除を取り消すメッセージ アクティビティ通知を取得するには、ハンドラー OnMessageUndelete オーバーライドできます。

削除されたメッセージが復元されたときに、 OnMessageUndelete を使用してメッセージ アクティビティを元に戻す通知の例を次に示します。

app.OnMessageUndelete(async context =>
{
    await context.Send("message is undeleted");
});
app.on('messageUndelete', async ({ activity, send }) => {
    const undeletedMessage = activity.text;
    await send(`Previously the message was deleted. After undeleting, the message is now: "${undeletedMessage}"`);
});
{
"type":"messageUpdate",
"timestamp":"2022-10-28T17:19:39.4615413Z",
"localTimestamp":"2022-10-28T10:19:39.4615413-07:00",
"id":"1666977568748",
"channelId":"msteams",
"serviceUrl":"https://canary.botapi.skype.com/amer/",
"from": {
    "id":"29:1BLjP9j3_TM4mubmQZsYEo7jDyLeLf_YVA9sVPVO7KMAFMjJWB_EUGveb9EVDh9LgoNp9qjnzEBy4kgw83Jf1Kg",
    "name":"Alex Wilber",
    "aadObjectId":"976e4d1e-2108-43ee-a092-46a9507c5606"
},
"conversation":{
    "conversationType":"personal",
    "tenantId":"528dbe3f-15e0-4e37-84a1-00cc305847dd","id":"a:1tewuGJ44RkB90tiJNQ_I4q8vyuN5CYA_f-v6f0Vd-Bs3Ce85C73Ah1y8TvyjESsTHWjjgw-gnsuIuCUOWkfOCq6qaUYsk2_-fj93XXXHUMAUzhFFvTnaCU7V4WiMqXQL"
},
"recipient":{
    "id":"28:0d469698-ab9d-479a-b0d8-758b6e6b1234",
    "name":"Testbot"
},
"entities":[
    {
           "locale":"en-US",
        "country":"US",
        "platform":"Web",
        "timezone":"America/Los_Angeles",
        "type":"clientInfo"
    }
],
"channelData":{
    "eventType":"undeleteMessage",
    "tenant":{"id":"528dbe3f-15e0-4e37-84a1-00cc305847dd"}
},
"locale":"en-US",
"localTimezone":"America/Los_Angeles"
}  
PUT {Service URL of your agent}/v3/conversations/{conversationId}/activities/{activityId}
{
    "type": "message",
    "text": "This message has been updated"
}

論理的な削除メッセージ アクティビティを受信する

メッセージを論理的に削除すると、エージェントは論理的な削除メッセージ アクティビティの通知を受け取ります。

エージェントで論理的な削除メッセージ アクティビティ通知を取得するには、ハンドラー OnMessageSoftDelete オーバーライドできます。

次の例は、メッセージが論理的に削除されたときに OnMessageSoftDelete を使用した論理的な削除メッセージ アクティビティ通知を示しています。

app.OnMessageSoftDelete(async context =>
{
    await context.Send("message is soft deleted");
}); 
app.on('messageSoftDelete', async ({ activity, send }) => {
    const messageId = activity.id;
    await send(`The deleted message id is ${messageId}`);
});

{
"type":"messageDelete",
"timestamp":"2022-10-28T17:19:43.1612052Z",
"localTimestamp":"2022-10-28T10:19:43.1612052-07:00",
"id":"1666977568748",
"channelId":"msteams",
"serviceUrl":"https://canary.botapi.skype.com/amer/",
"from": {
    "id":"29:1BLjP9j3_TM4mubmQZsYEo7jDyLeLf_YVA9sVPVO7KMAFMjJWB_EUGveb9EVDh9LgoNp9qjnzEBy4kgw83Jf1Kg",
    "name":"Alex Wilber",
    "aadObjectId":"976e4d1e-2108-43ee-a092-46a9507c5606"
},
"conversation":{
    "conversationType":"personal",
    "tenantId":"528dbe3f-15e0-4e37-84a1-00cc305847dd","id":"a:1tewuGJ44RkB90tiJNQ_I4q8vyuN5CYA_f-v6f0Vd-Bs3Ce85C73Ah1y8TvyjESsTHWjjgw-gnsuIuCUOWkfOCq6qaUYsk2_-fj93XXXHUMAUzhFFvTnaCU7V4WiMqXQL"
},
"recipient":{
    "id":"28:0d469698-ab9d-479a-b0d8-758b6e6b1235",
    "name":"Testagent"
},
"entities":[
    {
        "locale":"en-US",
        "country":"US",
        "platform":"Web",
        "timezone":"America/Los_Angeles",
        "type":"clientInfo"
    }
],
"channelData":{
    "eventType":"softDeleteMessage",
    "tenant":{"id":"528dbe3f-15e0-4e37-84a1-00cc305847dd"}
},
"locale":"en-US",
"localTimezone":"America/Los_Angeles"
}  

エージェントから送信されたメッセージを更新および削除する

重要

このセクションのコード サンプルは、バージョン 4.6 以降のバージョンの Bot Framework SDK に基づいています。 以前のバージョンのドキュメントをお探しの場合は、ドキュメントのレガシ SDK フォルダーの ボット - v3 SDK セクションを参照してください。

エージェントは、データの静的スナップショットとしてではなく、送信後にメッセージを動的に更新できます。 Teams SDK Framework の context.Api.Conversations.Activities.DeleteAsync(...) メソッドを使用してメッセージを削除することもできます。

注:

エージェントは、Microsoft Teamsでユーザーによって送信されたメッセージを更新または削除できません。

メッセージを更新する

ポーリングの更新、ボタンを押した後の使用可能なアクションの変更、その他の非同期状態の変更などのシナリオでは、動的メッセージ更新を使用できます。

新しいメッセージが元の種類と一致する必要はありません。 たとえば、元のメッセージに添付ファイルが含まれている場合、新しいメッセージは単純なテキスト メッセージにすることができます。

サンプル コード リファレンス

既存のメッセージを更新するには、既存のアクティビティ ID を持つ新しい Activity オブジェクトをコンテキストに渡します。Api.Conversations.Activities.UpdateAsync(...)method of theTurnContext' クラス。

app.OnMessage(async context =>
{
    // Send initial message
    var response = await context.Send("Your Message");
    var conversationId = context.Activity.Conversation.Id;
    var activityId = response.Id;

    var updatedActivity = new MessageActivity("The new text for the activity");

    await context.Api.Conversations.Activities.UpdateAsync(conversationId, activityId, updatedActivity);
});

既存のメッセージを更新するには、既存のアクティビティ ID を持つ新しい Activity オブジェクトを TurnContext オブジェクトの updateActivity メソッドに渡します。

app.on('message', async ({ activity, api, send }) => {
    // Send initial message
    const response = await send('Your Message');
    const conversationId = activity.conversation.id;
    const activityId = response.id;

    await api.conversations.activities(conversationId).update(activityId, {
        type: 'message',
        text: 'The new text for the activity'
    });
});

既存のメッセージを更新するには、既存のアクティビティ ID を持つ新しい Activity オブジェクトを TurnContext クラスの context.Api.Conversations.Activities.UpdateAsync(...) メソッドに渡します。

@app.on_message
async def handle_message(ctx: ActivityContext[MessageActivity]):
    # Send initial message
    response = await ctx.send("Your Message")
    conversation_id = ctx.activity.conversation.id
    activity_id = response.id

    await ctx.api.conversations.activities(conversation_id).update(
        activity_id, MessageActivityInput(text="The new text for the activity")
    )

注:

任意の Web プログラミング技術で Teams アプリを開発し、Bot Framework REST API を直接呼び出すことができますが、すべてのトークン処理を自分で実行する必要があります。 これを行うには、API 要求で [認証] セキュリティ手順を実装する必要があります。

会話内の既存のアクティビティを更新するには、リクエスト エンドポイントに conversationIdactivityId を含めます。 このシナリオを完了するには、元の POST 呼び出しによって返されたアクティビティ ID をキャッシュする必要があります。

PUT /v3/conversations/{conversationId}/activities/{activityId}
要求 応答
Activity オブジェクト。 ResourceResponse オブジェクト。

メッセージを更新したので、受信アクティビティのボタン選択時に既存のカードを更新します。

カードを更新する

ボタン選択時に既存のカードを更新するには、着信アクティビティの ReplyToIdを使用できます。

サンプル コード リファレンス

ボタン選択で既存のカードを更新するには、更新されたカードとアクティビティ ID として ReplyToId を含む新しい Activity オブジェクトを TurnContext クラスの context.Api.Conversations.Activities.UpdateAsync(...) メソッドに渡します。

app.OnMessage(async context =>
{
    var conversationId = context.Activity.Conversation.Id;
    var activityId = context.Activity.ReplyToId;

    var updatedActivity = new MessageActivity();
    updatedActivity.Attachments.Add(card.ToAttachment());

    await context.Api.Conversations.Activities.UpdateAsync(conversationId, activityId, updatedActivity);
});

ボタン選択で既存のカードを更新するには、更新されたカードとアクティビティ ID として replyToId を含む新しい Activity オブジェクトを TurnContext オブジェクトの updateActivity メソッドに渡します。

app.on('message', async ({ activity, api }) => {
    const conversationId = activity.conversation.id;
    const activityId = activity.replyToId;

    await api.conversations.activities(conversationId).update(activityId, {
        type: 'message',
        attachments: [card]
    });
});

ボタン クリックで既存のカードを更新するには、更新されたカードとアクティビティ ID として reply_to_id を含む新しい Activity オブジェクトを TurnContext クラスの ctx.api.conversations.activities(conversation_id).update(...) メソッドに渡します。

@app.on_message
async def handle_update_card(ctx: ActivityContext[MessageActivity]):
    conversation_id = ctx.activity.conversation.id
    activity_id = ctx.activity.reply_to_id

    await ctx.api.conversations.activities(conversation_id).update(
        activity_id, MessageActivityInput().add_card(card)
    )

注:

任意の Web プログラミング技術で Teams アプリを開発し、Bot Framework REST API を直接呼び出すことができますが、すべてのトークン処理を自分で実行する必要があります。 これを行うには、API 要求で [認証] セキュリティ手順を実装する必要があります。

会話内の既存のアクティビティを更新するには、リクエスト エンドポイントに conversationIdactivityId を含めます。 このシナリオを完了するには、元の POST 呼び出しによって返されたアクティビティ ID をキャッシュする必要があります。

PUT /v3/conversations/{conversationId}/activities/{activityId}
要求 応答
Activity オブジェクト。 ResourceResponse オブジェクト。

カードを更新したら、Teams SDK Framework を使用してメッセージを削除できます。

メッセージを削除する

Teams SDK Framework では、すべてのメッセージに固有のアクティビティ識別子があります。 メッセージは、Teams SDK Framework の context.Api.Conversations.Activities.DeleteAsync(...) メソッドを使用して削除できます。

サンプル コード リファレンス

メッセージを削除するには、そのアクティビティの ID を TurnContext クラスの context.Api.Conversations.Activities.DeleteAsync(...) メソッドに渡します。

app.OnMessage(async context =>
{
    var conversationId = context.Activity.Conversation.Id;

    foreach (var activityId in _list)
    {
        await context.Api.Conversations.Activities.DeleteAsync(conversationId, activityId);
    }
});

サンプル コード リファレンス

メッセージを削除するには、そのアクティビティの ID を TurnContext オブジェクトの context.Api.Conversations.Activities.DeleteAsync(...) メソッドに渡します。

app.on('message', async ({ activity, api }) => {
    const conversationId = activity.conversation.id;

    for (const activityId of activityIds) {
        await api.conversations.activities(conversationId).delete(activityId);
    }
});

そのメッセージを削除するには、そのアクティビティの ID を TurnContext オブジェクトの delete_activity メソッドに渡します。

@app.on_message
async def handle_delete(ctx: ActivityContext[MessageActivity]):
    conversation_id = ctx.activity.conversation.id

    for activity_id in _list:
        await ctx.api.conversations.activities(conversation_id).delete(activity_id)

会話内の既存のアクティビティを削除するには、リクエスト エンドポイントに conversationIdactivityId を含めます。

DELETE /v3/conversations/{conversationId}/activities/{activityId}
要求および応答 [説明]
該当なし 操作の結果を示す HTTP 状態コード。 応答の本文には何も指定されません。

引用符で囲まれた返信

引用符で囲まれた応答を使用すると、エージェントは会話内の前のメッセージを参照できます。 ユーザーが別のメッセージを引用するメッセージを送信すると、エージェントは引用符で囲まれたコンテンツに関する構造化されたメタデータを受け取ります。 エージェントは、前のメッセージを引用するメッセージを送信することもできます。

見積もり返信を受け取る

ユーザーがメッセージを引用符で囲んでエージェントに送信すると、受信アクティビティで引用符で囲まれた応答メタデータを使用できます。 GetQuotedMessages メソッドを使用して、引用符で囲まれたすべての応答エンティティにアクセスします。

app.OnMessage(async context =>
{
    var quotes = context.Activity.GetQuotedMessages();

    if (quotes.Count > 0)
    {
        var quote = quotes[0].QuotedReply;
        await context.Reply(
            $"You quoted message {quote.MessageId} from {quote.SenderName}: \"{quote.Preview}\"");
    }
});

ユーザーがメッセージを引用符で囲んでエージェントに送信すると、受信アクティビティで引用符で囲まれた応答メタデータを使用できます。 getQuotedMessages メソッドを使用して、引用符で囲まれたすべての応答エンティティにアクセスします。

app.on('message', async ({ activity, reply }) => {
  const quotes = activity.getQuotedMessages();

  if (quotes.length > 0) {
    const quote = quotes[0].quotedReply;
    await reply(
      `You quoted message ${quote.messageId} from ${quote.senderName}: "${quote.preview}"`
    );
  }
});

ユーザーがメッセージを引用符で囲んでエージェントに送信すると、受信アクティビティで引用符で囲まれた応答メタデータを使用できます。 get_quoted_messages メソッドを使用して、引用符で囲まれたすべての応答エンティティにアクセスします。

@app.on_message
async def handle_message(ctx: ActivityContext[MessageActivity]):
    quotes = ctx.activity.get_quoted_messages()

    if quotes:
        quote = quotes[0].quoted_reply
        await ctx.reply(
            f"You quoted message {quote.message_id} from {quote.sender_name}: \"{quote.preview}\""
        )

見積もり返信を送信する

エージェントが Reply()を呼び出すと、SDK は受信メッセージを参照する引用符で囲まれた応答エンティティに自動的にスタンプを付けます。 返信は Teams で見積もり返信として表示されます。

app.OnMessage(async context =>
{
    // Reply() automatically quotes the inbound message
    await context.Reply("Got it!");
});

同じ会話 (受信メッセージではなく) で別のメッセージを引用するには、 Quote() メソッドを使用して、引用符で囲むメッセージ ID を指定します。

app.OnMessage(async context =>
{
    // Quote a specific message by its ID
    var parentMessageId = "1772050244572";
    await context.Quote(parentMessageId, "Referencing an earlier message");
});

エージェントが reply()を呼び出すと、SDK は受信メッセージを参照する引用符で囲まれた応答エンティティに自動的にスタンプを付けます。 返信は Teams で見積もり返信として表示されます。

app.on('message', async ({ reply }) => {
  // reply() automatically quotes the inbound message
  await reply('Got it!');
});

同じ会話 (受信メッセージではなく) で別のメッセージを引用するには、 quote() メソッドを使用して、引用符で囲むメッセージ ID を指定します。

app.on('message', async ({ quote }) => {
  // Quote a specific message by its ID
  const parentMessageId = '1772050244572';
  await quote(parentMessageId, 'Referencing an earlier message');
});

エージェントが reply()を呼び出すと、SDK は受信メッセージを参照する引用符で囲まれた応答エンティティに自動的にスタンプを付けます。 返信は Teams で見積もり返信として表示されます。

@app.on_message
async def handle_message(ctx: ActivityContext[MessageActivity]):
    # reply() automatically quotes the inbound message
    await ctx.reply("Got it!")

同じ会話 (受信メッセージではなく) で別のメッセージを引用するには、 quote() メソッドを使用して、引用符で囲むメッセージ ID を指定します。

@app.on_message
async def handle_message(ctx: ActivityContext[MessageActivity]):
    # Quote a specific message by its ID
    parent_message_id = "1772050244572"
    await ctx.quote(parent_message_id, "Referencing an earlier message")

プロアクティブにメッセージを送信するための引用符で囲まれた応答を作成する

プロアクティブなシナリオ ( app.Send()を使用) または複数のメッセージを引用する場合は、メッセージ アクティビティで AddQuote() メソッドを使用します。 メッセージ ID とオプションの応答テキストを渡します。

var parentMessageId = "1772050244572";
var firstMessageId = "1772050244573";
var secondMessageId = "1772050244574";

// Single quote with response below it
var msg = new MessageActivity()
    .AddQuote(parentMessageId, "Here is my response");
await app.Send(conversationId, msg);

// Multiple quotes with interleaved responses
msg = new MessageActivity()
    .AddQuote(firstMessageId, "response to first")
    .AddQuote(secondMessageId, "response to second");
await app.Send(conversationId, msg);

// Grouped quotes — omit response to group quotes together
msg = new MessageActivity("see below for previous messages")
    .AddQuote(firstMessageId)
    .AddQuote(secondMessageId, "response to both");
await app.Send(conversationId, msg);

プロアクティブなシナリオ ( app.send()を使用) または複数のメッセージを引用する場合は、メッセージ アクティビティで addQuote() メソッドを使用します。 メッセージ ID とオプションの応答テキストを渡します。

import { MessageActivity } from '@microsoft/teams.api';

const parentMessageId = '1772050244572';
const firstMessageId = '1772050244573';
const secondMessageId = '1772050244574';

// Single quote with response below it
let msg = new MessageActivity()
  .addQuote(parentMessageId, 'Here is my response');
await app.send(conversationId, msg);

// Multiple quotes with interleaved responses
msg = new MessageActivity()
  .addQuote(firstMessageId, 'response to first')
  .addQuote(secondMessageId, 'response to second');
await app.send(conversationId, msg);

// Grouped quotes — omit response to group quotes together
msg = new MessageActivity('see below for previous messages')
  .addQuote(firstMessageId)
  .addQuote(secondMessageId, 'response to both');
await app.send(conversationId, msg);

プロアクティブなシナリオ ( app.send()を使用) または複数のメッセージを引用する場合は、メッセージ アクティビティで add_quote() メソッドを使用します。 メッセージ ID とオプションの応答テキストを渡します。

from microsoft_teams.api.activities.message import MessageActivityInput

parent_message_id = "1772050244572"
first_message_id = "1772050244573"
second_message_id = "1772050244574"

# Single quote with response below it
msg = (MessageActivityInput()
    .add_quote(parent_message_id, "Here is my response"))
await app.send(conversation_id, msg)

# Multiple quotes with interleaved responses
msg = (MessageActivityInput()
    .add_quote(first_message_id, "response to first")
    .add_quote(second_message_id, "response to second"))
await app.send(conversation_id, msg)

# Grouped quotes — omit response to group quotes together
msg = (MessageActivityInput(text="see below for previous messages")
    .add_quote(first_message_id)
    .add_quote(second_message_id, "response to both"))
await app.send(conversation_id, msg)

Teams チャネル データでメッセージを送信する

channelData オブジェクトには Teams 固有の情報が含まれており、チーム ID とチャネル ID の決定的なソースです。 必要に応じて、これらの ID をキャッシュし、ローカル ストレージのキーとして使用できます。 SDK の App は、 channelData オブジェクトから重要な情報を取得してアクセスできるようにします。 ただし、 turnContext オブジェクトから元のデータにいつでもアクセスできます。

channelData オブジェクトは、チャネルの外部で行われるので、個人的な会話のメッセージには含まれません。

エージェントに送信されるアクティビティの一般的な channelData オブジェクトには、次の情報が含まれています。

  • eventType: Teams エージェントで会話イベントが発生した場合にのみ、 Teams イベントの種類が渡されます。
  • tenant.id: すべてのコンテキストで渡されたテナント ID をMicrosoft Entraします。
  • team: 個人用チャットではなく、チャネル コンテキストでのみ渡されます。
  • channel: エージェントがメンションされたとき、またはエージェントが追加されるチーム内のチャネルのイベントに対してのみ、チャネル コンテキストで渡されます。
    • id: チャネルの GUID。
    • name: チャネル 変更イベントの場合にのみ渡されるチャネル名。
  • channelData.teamsTeamId:廃止。 このプロパティは、下位互換性のためにのみ含まれています。
  • channelData.teamsChannelId:廃止。 このプロパティは、下位互換性のためにのみ含まれています。

次のコードは、channelData オブジェクト (channelCreated イベント) の例を示しています。

"channelData": {
    "eventType": "channelCreated",
    "tenant": {
        "id": "72f988bf-86f1-41af-91ab-2d7cd011db47"
    },
    "channel": {
        "id": "19:693ecdb923ac4458a5c23661b505fc84@thread.skype",
        "name": "My New Channel"
    },
    "team": {
        "id": "19:693ecdb923ac4458a5c23661b505fc84@thread.skype"
    }
}

Teamsチャネルデータ

channelData オブジェクトには Teams 固有の情報が含まれており、チーム ID とチャネル ID の決定的なソースです。 必要に応じて、これらの ID をキャッシュし、ローカル ストレージのキーとして使用できます。 SDK の App は、 channelData オブジェクトから重要な情報を取得してアクセスできるようにします。 ただし、 turnContext オブジェクトから元のデータにいつでもアクセスできます。

channelData オブジェクトは、チャネルの外部で行われるので、個人的な会話のメッセージには含まれません。

エージェントに送信されるアクティビティの一般的な channelData オブジェクトには、次の情報が含まれています。

  • eventType: チャネル変更イベントの場合にのみ、Teams イベントの種類が渡されます。
  • tenant.id: すべてのコンテキストで渡されたテナント ID をMicrosoft Entraします。
  • team: 個人用チャットではなく、チャネル コンテキストでのみ渡されます。
    • id: チャネルの GUID。
    • name: (how-to/conversations/subscribe-to-conversation-events.md#team-renamed) の場合にのみ渡されたチームの名前。
  • channel: エージェントがメンションされたとき、またはエージェントが追加されるチーム内のチャネルのイベントに対してのみ、チャネル コンテキストで渡されます。
    • id: チャネルの GUID。
    • name: チャネル 変更イベントの場合にのみ渡されるチャネル名。
  • channelData.teamsTeamId:廃止。 このプロパティは、下位互換性のためにのみ含まれています。
  • channelData.teamsChannelId:廃止。 このプロパティは、下位互換性のためにのみ含まれています。

channelData オブジェクトの例

次のコードは、channelData オブジェクト (channelCreated イベント) の例を示しています。

"channelData": {
    "eventType": "channelCreated",
    "tenant": {
        "id": "72f988bf-86f1-41af-91ab-2d7cd011db47"
    },
    "channel": {
        "id": "19:693ecdb923ac4458a5c23661b505fc84@thread.skype",
        "name": "My New Channel"
    },
    "team": {
        "id": "19:693ecdb923ac4458a5c23661b505fc84@thread.skype"
    }
}

エージェントの会話型 API からの状態コード

Teams アプリでこれらのエラーを適切に処理してください。 次の表に、エラー コードと、エラーが生成される説明を示します。

状態コード エラー コードとメッセージ値 説明 再試行要求 開発者アクション
400 コード: Bad Argument
メッセージ: *シナリオ固有
エージェントによって提供される要求ペイロードが無効です。 詳細については、「エラー メッセージ」を参照してください。 不要 エラーの要求ペイロードを再評価します。 詳細については、返されたエラー メッセージを確認してください。
401 コード: BotNotRegistered
メッセージ: このエージェントの登録が見つかりません。
このエージェントの登録が見つかりませんでした。 不要 エージェント ID とパスワードを確認します。 ボット ID (Microsoft Entra ID) が Teams 開発者ポータルに登録されているか、'Teams' チャネルが有効になっているAzureのボット チャネル登録Azure使用して登録されていることを確認します。
403 コード: BotDisabledByAdmin
メッセージ: テナント管理者がこのエージェントを無効にしました
管理ユーザーとエージェント アプリ間の相互作用がブロックされました。 管理は、アプリ ポリシー内のユーザーのアプリを許可する必要があります。 詳細については、「 アプリ ポリシー」を参照してください。 不要 エージェントとの対話が、エージェントがブロックされなくなったことを示す会話内のユーザーによって明示的に開始されるまで、会話への投稿を停止します。
403 コード: BotNotInConversationRoster
メッセージ: エージェントは会話名簿の一部ではありません。
エージェントは会話の一部ではありません。 会話でアプリを再インストールする必要があります。 不要 別の会話要求を送信する前に、エージェントが再度追加されたことを示す installationUpdate イベントを待ちます。
403 コード: ConversationBlockedByUser
メッセージ: ユーザーはエージェントとの会話をブロックしました。
ユーザーは、モデレート設定を使用して、個人用チャットまたはチャネルでエージェントをブロックしました。 不要 キャッシュから会話を削除します。 エージェントとの対話が会話のユーザーによって明示的に開始され、エージェントがブロックされなくなったことを示すまで、会話への投稿を停止します。
403 コード: ForbiddenOperationException
メッセージ: エージェントがユーザーの個人用スコープにインストールされていない
プロアクティブ メッセージは、個人用スコープにインストールされていないエージェントによって送信されます。 不要 別の会話要求を送信する前に、個人用スコープでアプリをインストールします。
403 コード: InvalidBotApiHost
メッセージ: エージェント API ホストが無効です。 GCC テナントの場合は、 https://smba.infra.gcc.teams.microsoft.comを呼び出します。
GCC テナントに属する会話のパブリック API エンドポイントと呼ばれるエージェント。 不要 会話のサービス URL を更新して https://smba.infra.gcc.teams.microsoft.com し、要求を再試行します。
403 コード: NotEnoughPermissions
メッセージ: *シナリオ固有
エージェントには、要求されたアクションを実行するための必要なアクセス許可がありません。 不要 エラー メッセージから必要なアクションを決定します。
404 コード: ActivityNotFoundInConversation
メッセージ: 会話が見つかりません。
指定されたメッセージ ID が会話で見つかりませんでした。 メッセージが存在しないか、削除されます。 不要 送信されるメッセージ ID が予期される値であるかどうかを確認します。 キャッシュされた場合は、ID を削除します。
404 コード: ConversationNotFound
メッセージ: 会話が見つかりません。
会話が見つからなかったのは、存在しないか削除されているためです。 不要 送信された会話 ID が予期される値であるかどうかを確認します。 キャッシュされた場合は、ID を削除します。
412 コード: PreconditionFailed
メッセージ: 前提条件に失敗しました。もう一度お試しください。
同じ会話に対する複数の同時操作が原因で、いずれかの依存関係で前提条件が失敗しました。 はい 指数バックオフを使用して再試行します。
413 コード: MessageSizeTooBig
メッセージ: メッセージ サイズが大きすぎます。
受信要求のサイズが大きすぎます。 詳細については、「 エージェント メッセージの書式設定」を参照してください 不要 ペイロード サイズを小さくします。
429 コード: Throttled
メッセージ: 要求が多すぎます。 また、後で再試行するタイミングも返します。
エージェントによって送信された要求が多すぎます。 詳細については、「 レート制限」を参照してください。 はい Retry-After ヘッダーを使用して、バックオフ時間を確認してください。
500 コード: ServiceError
メッセージ: *各種
内部サーバー エラー。 不要 開発者コミュニティで問題を報告します。
開発者コミュニティ フォーラム。
502 コード: ServiceError
メッセージ: *各種
サービス依存関係の問題。 はい 指数バックオフを使用して再試行します。 問題が解決しない場合は、 開発者コミュニティ フォーラムで問題を報告してください。
503 サービスは使用できません。 はい 指数バックオフを使用して再試行します。 問題が解決しない場合は、 開発者コミュニティで問題を報告してください。
504 ゲートウェイのタイムアウト。 はい 指数バックオフを使用して再試行します。 問題が解決しない場合は、 開発者コミュニティで問題を報告してください。

状態コードの再試行ガイダンス

各状態コードの一般的な再試行ガイダンスを次の表に示します。エージェントは、指定されていない状態コードの再試行を避ける必要があります。

状態コード 再試行戦略
403 InvalidBotApiHostの GCC API https://smba.infra.gcc.teams.microsoft.comを呼び出して再試行します。
412 指数バックオフを使用して再試行します。
429 Retry-After ヘッダーを使用して再試行し、要求の間の待機時間 (使用可能な場合) を秒単位で判断します。 それ以外の場合は、可能であれば、スレッド ID で指数バックオフを使用して再試行してください。
502 指数バックオフを使用して再試行します。
503 指数バックオフを使用して再試行します。
504 指数バックオフを使用して再試行します。

エージェントの要求ヘッダー

エージェントへの現在の送信要求には、ペイロード全体をアンパックすることなく、エージェントがトラフィックをルーティングするのに役立つ情報がヘッダーまたは URL に含まれません。 アクティビティは、https://<your_domain>/api/messages のような URL を使用してエージェントに送信されます。 ヘッダーに会話 ID とテナント ID を表示する要求を受信します。

要求ヘッダー フィールド

非同期フローと同期フローの両方について、エージェントに送信されるすべての要求に 2 つの標準以外の要求ヘッダー フィールドが追加されます。 次の表に、要求ヘッダー フィールドとその値を示します。

フィールド キー
x-ms-conversation-id 該当する場合は要求アクティビティに対応し、確認または検証された会話 ID。
x-ms-tenant-id 要求アクティビティの会話に対応するテナント ID。

テナントまたは会話 ID がアクティビティに存在しない場合、またはサービス側で検証されなかった場合、値は空です。

画像はヘッダー フィールドを示しています。

記載されているメッセージのみを受信する

エージェントが @mentionedされているチャネルまたはチャット メッセージのみをエージェントが取得できるようにするには、メッセージをフィルター処理する必要があります。 エージェントが @mentionedされているメッセージのみを受信できるようにするには、次のコード スニペットを使用します。

  app.OnMessage(async context =>
{
    if (!context.Activity.GetMentions().Any(mention => mention.Mentioned.Id.Equals(context.Activity.Recipient.Id, StringComparison.OrdinalIgnoreCase)))
    {
        return;
    }

    await context.Send("Using RSC the agent can receive messages across channels or chats in team without being @mentioned.");
});

エージェントがすべてのメッセージを受信する場合は、 @mention メッセージをフィルター処理する必要はありません。

次の手順

エージェントとのチャネルチャットとグループ チャットの会話

関連項目

Teams エージェントの会話イベント